jmeter authorization manager / loop count - jmeter

I am using JMeter to run some web service tests and here is my problem:
Here is my test structure:
> Threadgroup
> -User Defined Variables
> -SOAP/XML-RPC Request1
> ->Xpath Extractor
> ->Beanshell PostProcessor
> -SOAP/XML-RPC Request2
> ->HTTP Authorization manager
> ->Beanshell PreProcessor
> -Debug Sampler
> View Results Tree
The Xpath Extractor processes Request1 results to extract a password and the Beanshell PostProcessor stores it in a User Defined Variable "Authorization".
The Beanshell PreProcessor extracts the password from the variable, sets it with a user in the authorization manager.
AuthManager authmanager = sampler.getAuthManager();
Authorization authorization = new Authorization();
authorization.setURL("http://localhost:1130");
authorization.setUser("user");
authorization.setPass(vars.get("Authorization"));
authorization.setRealm("gSOAP Web Service");
authmanager.addAuth(authorization);
Everything works perfectly until I need to loop the group thread. The first loop works fine but the rest of the execution fails. It seems as if on the second round the Authorization Manager is not updated with new credentials which causes the following requests to fail.
I tried clearing the Authorization manager before creating the new authorization but it's not working.
Help!!

addAuth method (as can be seeing in code) will only add an Authorization object if it doesn't yet exist. So before adding the object for the second time you have to remove it, something like this:
AuthManager authmanager = sampler.getAuthManager();
// ... (same as before)
authorization.setRealm("gSOAP Web Service");
for (int i = 0; i < authmanager.getAuthCount(); i++) {
Authorization oldAuthorization = authmanager.get(i);
if (oldAuthorization == null) {
continue;
}
if (oldAuthorization.getURL().equals(authorization.getURL())) {
authmanager.remove(i);
}
}
authmanager.addAuth(authorization);

Related

Avoid Hashmap overwriting in Jmeter with multiple requests

I have added a Beanshell post processor in a Jmeter thread group and I have multiple HTTP requests in the same thread group that uses the post processor. I am creating a Hashmap and putting values into the map after execution of every HTTP request and then creating a json object of the map. The issue is that after every HTTP request, the map values are getting replaced by only the latest values of the last HTTP request that was executed. How to avoid this?
The code in my postprocessor is as follows:
HashMap map = new HashMap();
map.put(extID,intID);
object = new JSONObject(map);
vars.put("idMap" , object.toString());
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject)parser.parse(vars.get("idMap"));
You can create a map and share with all threads using props, example:
Map<String, String> map = props.get("map")
if (map == null) {
map = new HashMap<>()
} else {
// in your case map.put(extID,intID);
String uuid = UUID.randomUUID().toString().replace("-", "")
map.put(uuid,uuid)
}
props.put("map", map)
vars.putObject("map", map)
JMeter's Post-Processors obey JMeter Scoping Rules so if you place the PostProcessor at the same level as Samplers - the PostProcessor will be executed after each sampler
If you want it to be executed only after a specific Sampler - make the PostProcessor a direct child of this sampler
Also be aware that since JMeter 3.1 it's recommended to use JSR223 Test Elements and Groovy language for scripting so it worth considering migrating, you won't have to change a single line of your code if you want, or alternatively Groovy has built-in JSON support

Fabric8 customResourceDefinitions test

I am working on Fabric8 unit test, now I am trying to create a CRD against KubernetesServer.
import io.fabric8.kubernetes.api.model.apiextensions.v1.CustomResourceDefinition;
public class TestCertManagerService {
#Rule
public KubernetesServer server = new KubernetesServer();
#Test
#DisplayName("Should list all CronTab custom resources")
public void testCronTabCrd() throws IOException {
// Given
//server.expect().get().withPath("/apis/stable.example.com/v1/namespaces/default/crontabs").andReturn(HttpURLConnection.HTTP_OK, ?????).once();
KubernetesClient client = server.getClient();
CustomResourceDefinition cronTabCrd = client.apiextensions().v1().customResourceDefinitions()
.load(new BufferedInputStream(new FileInputStream("src/test/resources/crontab-crd.yml")))
.get();
client.apiextensions().v1().customResourceDefinitions().create(cronTabCrd);
}
}
When I ran it, I got the following error
TestCertManagerService > testCronTabCrd FAILED
io.fabric8.kubernetes.client.KubernetesClientException: Failure executing: GET at: https://localhost:60690/apis/apiextensions.k8s.io/v1/customresourcedefinitions.
at app//io.fabric8.kubernetes.client.dsl.base.OperationSupport.requestFailure(OperationSupport.java:694)
at app//io.fabric8.kubernetes.client.dsl.base.OperationSupport.requestFailure(OperationSupport.java:673)
at app//io.fabric8.kubernetes.client.dsl.base.OperationSupport.assertResponseCode(OperationSupport.java:626)
at app//io.fabric8.kubernetes.client.dsl.base.OperationSupport.handleResponse(OperationSupport.java:566)
at app//io.fabric8.kubernetes.client.dsl.base.OperationSupport.handleResponse(OperationSupport.java:527)
at app//io.fabric8.kubernetes.client.dsl.base.OperationSupport.handleResponse(OperationSupport.java:510)
at app//io.fabric8.kubernetes.client.dsl.base.BaseOperation.listRequestHelper(BaseOperation.java:136)
at app//io.fabric8.kubernetes.client.dsl.base.BaseOperation.list(BaseOperation.java:505)
at app//io.fabric8.kubernetes.client.dsl.base.BaseOperation.list(BaseOperation.java:494)
at app//io.fabric8.kubernetes.client.dsl.base.BaseOperation.list(BaseOperation.java:87)
at app//com.ibm.si.qradar.cp4s.service.certmanager.TestCertManagerService.testCronTabCrd(TestCertManagerService.java:94)
I have a few of questions:
(1) In this case, I am using v1() interface, sometimes I saw example code is using v1beta1(), what decides this version? By the way, I am using Kubernetes-client library 5.9.0
(2) In my code , I comments out this line
server.expect().get().withPath("/apis/stable.example.com/v1/namespaces/default/crontabs").andReturn(HttpURLConnection.HTTP_OK, ?????).once();
What is this statement for? In my case, I want to load a CRD, then create a CR, what is "?????" in the statement?
Any ideas for stack trace? How to fix it?
I appreciate it in advance.
From the code which you shared, it looks like you're using Fabric8 Kubernetes Mock Server in expectations mode. Expectations mode requires the user to set the REST API expectations. So the code shown below is setting some expectations from Mock Server viewpoint.
// Given
server.expect().get()
.withPath("/apis/stable.example.com/v1/namespaces/default/crontabs")
.andReturn(HttpURLConnection.HTTP_OK, getCronTabList())
.once();
These are the expectations set:
Mock Server would be requested a GET request at this URL: /apis/stable.example.com/v1/namespaces/default/crontabs . From URL we can expect a resource under stable.example.com apigroup with v1 version, default namespace and crontabs as plural.
When this URL is being hit, you're also defining response code and response body in andReturn() method. First argument is the response code (200 in this case) and second argument is the response body (a List object of CronTab which would be serialized and sent as response by mock server).
This request is only hit .once(), if KubernetesClient created by Mock Server requests this endpoint more than once; the test would fail. If you want to hit the endpoint more than once, you can use .times(..) method instead.
But in your test I see you're loading a CustomResourceDefinition from YAML and creating it which doesn't seem to match the expectations you set earlier. If you're writing a test about creating a CustomResourceDefinition, it should look like this:
#Test
#DisplayName("Should Create CronTab CRD")
void testCronTabCrd() throws IOException {
// Given
KubernetesClient client = server.getClient();
CustomResourceDefinition cronTabCrd = client.apiextensions().v1()
.customResourceDefinitions()
.load(new BufferedInputStream(new FileInputStream("src/test/resources/crontab-crd.yml")))
.get();
server.expect().post()
.withPath("/apis/apiextensions.k8s.io/v1/customresourcedefinitions")
.andReturn(HttpURLConnection.HTTP_OK, cronTabCrd)
.once();
// When
CustomResourceDefinition createdCronTabCrd = client.apiextensions().v1()
.customResourceDefinitions()
.create(cronTabCrd);
// Then
assertNotNull(createdCronTabCrd);
}
Bdw, if you don't like setting REST expectations. Fabric8 Kubernetes Mock Server also has a CRUD mode which mock real Kubernetes APIServer. You can enable it like this:
#Rule
public KubernetesServer server = new KubernetesServer(true, true);
then use it in test like this:
#Test
#DisplayName("Should Create CronTab CRD")
void testCronTabCrd() throws IOException {
// Given
KubernetesClient client = server.getClient();
CustomResourceDefinition cronTabCrd = client.apiextensions().v1()
.customResourceDefinitions()
.load(new BufferedInputStream(new FileInputStream("src/test/resources/crontab-crd.yml")))
.get();
// When
CustomResourceDefinition createdCronTabCrd = client.apiextensions().v1()
.customResourceDefinitions()
.create(cronTabCrd);
// Then
assertNotNull(createdCronTabCrd);
}
I added CustomResourceLoadAndCreateTest and CustomResourceLoadAndCreateCrudTest tests in my demo repository: https://github.com/r0haaaan/kubernetes-mockserver-demo

Can't add to cookie manager after clearing

I have a preprocessing beanshell script in which I was previously adding cookies from the input file and it was working perfectly. I recently added a line to clear the cookies before adding them since I have multiple requests per loop now and don't want the response from the first request to set cookies on the second request.
import org.apache.jmeter.protocol.http.control.CookieManager;
import org.apache.jmeter.protocol.http.control.Cookie;
String value=vars.get("header");
if(value!=null && value!=""){
String cookiesStr=value.replace(";+",";");
String[] cookies= cookiesStr.split(";");
CookieManager manager = sampler.getCookieManager();
manager.clear();
for(int i=0;i<cookies.length;i++){
String[] cookieNameVal= cookies[i].split("=");
if(cookieNameVal.length>=2){
Cookie cookietidal = new Cookie(cookieNameVal[0],cookieNameVal[1],"domain1.com","/", false,-1);
Cookie cookievlg = new Cookie(cookieNameVal[0],cookieNameVal[1],"domain2.com","/", false,-1);
manager.add(cookie1);
manager.add(cookie2);
}
}
}
Now this clears the cookies, but it doesn't add any afterward. I can't figure out why.
I finally tried just creating a different instance of the cookie manager under each HTTP Request in my thread group. No need to clear them now.

ServiceStack user session not found when using sessionId in client Headers or Cookies

I am using ServiceStack v4 with custom Authentication. This is setup and working correctly. I can call the /auth service and get a returned AuthorizationResponse with unique SessionId.
I also have swagger-ui plugin setup. Using it, I can authenticate via /auth and then call one of my other services which require authentication without issue.
Now, from a secondary MVC application using the c# JsonServiceClient I can again successfully make a call to /auth and then secured services using the same client object. However, if I dispose of that client (after saving the unique sessionId to a cookie), then later create a new client, and either add the sessionId as a Cookie or via headers using x-ss-pid as documented, calling a services returns 401. If I call a non-secure service, but then try to access the unique user session, it returns a new session.
If I look at the request headers in that service, the cookie or Header is clearly set with the sessionId. The sessionId also exists in the sessionCache. The problem seems to be that the code which tries to get the session from the request isn't finding it.
To be more specific, it appears that ServiceExtensions.GetSessionId is looking at the HostContext and not the calling Request. I'm not sure why. Perhaps I misunderstand something along the way here.
If I directly try and fetch my expected session with the following code it's found without issue.
var req = base.Request;
var sessionId = req.GetHeader("X-" + SessionFeature.PermanentSessionId);
var sessionKey = SessionFeature.GetSessionKey(sessionId);
var session = (sessionKey != null ? Cache.Get<IAuthSession>(sessionKey) : null)?? SessionFeature.CreateNewSession(req, sessionId);
So, am I missing something obvious here? Or maybe not so obvious in creating my secondary client?
Sample code of client calls
Here is my authorization code. It's contained in a Controller class. This is just the relevant parts.
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
try
{
loginResult = client.Post(new Authenticate()
{
UserName = model.Email,
Password = model.Password,
RememberMe = model.RememberMe
});
Response.SetCookie(new HttpCookie(SessionFeature.PermanentSessionId, loginResult.SessionId));
return true;
}
}
Here is my secondary client setup and service call, contained in it's own controller class in another area of the MVC application
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
var cCookie = HttpContext.Request.Cookies.Get(SessionFeature.PermanentSessionId);
if (cCookie != null)
{
client.Headers.Add("X-" + SessionFeature.PermanentSessionId, cCookie.Value);
client.Headers.Add("X-" + SessionFeature.SessionOptionsKey, "perm");
}
response = client.Get(new SubscriptionStatusRequest());
}
Additional Update
During the Authenticate process the following function is called from HttpRequestExtensions with the name = SessionFeature.PermanentSessionId
public static class HttpRequestExtensions
{
/// <summary>
/// Gets string value from Items[name] then Cookies[name] if exists.
/// Useful when *first* setting the users response cookie in the request filter.
/// To access the value for this initial request you need to set it in Items[].
/// </summary>
/// <returns>string value or null if it doesn't exist</returns>
public static string GetItemOrCookie(this IRequest httpReq, string name)
{
object value;
if (httpReq.Items.TryGetValue(name, out value)) return value.ToString();
Cookie cookie;
if (httpReq.Cookies.TryGetValue(name, out cookie)) return cookie.Value;
return null;
}
Now what occurs is the httpReq.Items contains a SessionFeature.PermanentSessionId value, but I have no clue why and where this gets set. I don't even understand at this point what the Items container is on the IRequest. The code thus never gets to the functionality to check my cookies or headers
The Session wiki describes the different cookies used by ServiceStack Session.
If the client wants to use a Permanent SessionId (i.e. ss-pid), it also needs to send a ss-opt=perm Cookie to indicate it wants to use the permanent Session. This Cookie is automatically set when authenticating with the RememberMe=true option during Authentication.
There was an issue in the Session RequestFilter that was used to ensure Session Id's were attached to the current request weren't using the public IRequest.GetPermanentSessionId() API's which also looks for SessionIds in the HTTP Headers. This has been resolved with this commit which now lets you make Session requests using HTTP Headers, e.g:
//First Authenticate to setup an Authenticated Session with the Server
var client = new JsonServiceClient(BaseUrl);
var authResponse = client.Send(new Authenticate
{
provider = CredentialsAuthProvider.Name,
UserName = "user",
Password = "p#55word",
RememberMe = true,
});
//Use new Client instance without Session Cookies populated
var clientWithHeaders = new JsonServiceClient(BaseUrl);
clientWithHeaders.Headers["X-ss-pid"] = authResponse.SessionId;
clientWithHeaders.Headers["X-ss-opt"] = "perm";
var response = clientWithHeaders.Send(new AuthOnly()); //success
This fix is available from v4.0.37+ that's now available on MyGet.
However, if I dispose of that client (after saving the unique sessionId to a cookie)
If the client is disposed where is the cookie you are saving the sessionId located? This answer might provide some additional information.
then later create a new client, and either add the sessionId as a Cookie or via headers using x-ss-pid as documented, calling a services returns 401
If you store/save a valid sessionId as a string you should be able to supply it within a CookieContainer of a new client (given the sessionId is still authenticated). I know you said you tried adding the sessionId as a Cookie but I don't a see sample within your question using the CookieContainer so it should look something like...
using (var client = new JsonServiceClient(WebHelper.BuildApiUrl(Request)))
{
var cCookieId = savedCookieId; //a string that I believe you saved from a successfully authenticated client that is now disposed
if (cCookieId != null)
{
var cookie = new Cookie(SessionFeature.PermanentSessionId, cCookieId);
//cookie.Domian = "somedomain.com" //you will probably need to supply this as well
client.CookieContainer.Add(cookie)
}
response = client.Get(new SubscriptionStatusRequest());
}

How can I access form post parameters from a web test recorder plugin

Using Visual Studio 2012 to create web tests.
How can I access form post parameters from a web test recorder plugin? Most aspects of a recorded webtest are visible via Visual Studio's intelisense or from MSDN, but I cannot find the form post parameters.
I am testing a web site that uses form post parameters in an "interesting" manner. So far we have hand-edited the XML in the .webtest file, but that is error prone. So I would like to modify them in a web test recorder plugin.
Form post parameters can be accessed in a web test recorder plugin via the body field of a web request, but the body needs to be cast to the correct type. A recorder plugin provides has the recorded web test as a (field of a) parameter, the Items of the test include the individual requests. They also include comments and so on. An Item that is a WebTestRequest may have a Body field that, after casting, provides the form post parameters. This code shows a plugin that displays some details of the form post parameters via a WriteLine method that is not shown here. The inner loop can be replaced with code to modify, or delete or add new form post parameters.
public override void PostWebTestRecording(object sender, PostWebTestRecordingEventArgs e)
{
foreach (WebTestItem wti in e.RecordedWebTest.Items)
{
WebTestRequest wtiwtr = wti as WebTestRequest;
if (wtiwtr != null)
{
FormPostHttpBody formBody = wtiwtr.Body as FormPostHttpBody;
if (formBody == null)
{
// no formBody.
}
else
{
WriteLine("Have {0} form post parameters", formBody.FormPostParameters.Count);
foreach (FormPostParameter fpp in formBody.FormPostParameters)
{
WriteLine("FPP '{0}' = '{1}'", fpp.Name, fpp.Value);
}
}
}
}
}
Several other parts of the recorded web test can be accessed via these casts of wti in the code.
Comment wtic = wti as Comment;
IncludedWebTest wtiiwt = wti as IncludedWebTest;
SharepointInformation wtispi = wti as SharepointInformation;
TransactionTimer wtitt = wti as TransactionTimer;
WebTestConditionalConstruct wtiwtcc = wti as WebTestConditionalConstruct;
It is a little ugly but I was able to find it while debugging a plugin that uses the PreRequestDataBinding method. Putting my mouse over 'e', expand request -> body -> [Microsoft.VisualStudio.TestTools.WebTesting.FormPostHttpBody] -> FormPostParameters. But due to the protection level you won't be able to edit the value.
What I ended up doing was convert a web test into a coded web test. You can do this by right-clicking on the top level of the web test designer view, then choosing Generate Code. Looking at the coded web test, will allow you to modify the form post parameters. Look for something like this...
request1Body.FormPostParameters.Add("YourFormPostName", "value");
You can add form post parameter like this in PreRequest method of WebTestRequestPlugin -
FormPostHttpBody form = new FormPostHttpBody();
FormPostParameter par = new FormPostParameter();
par.Name = "name";
par.Value = "1";
form.add(par);
e.Request.Body = form;

Resources