I'm trying to implement a Server Sent Event Controller for updating my Web Browser Client with the newest Data to display.
This is my current Controller which sends the list of my data every 5 seconds. I want to send a SSE everytime I save my data in another service.
I read about using a channel, but how do I consume it with a Flux?
#GetMapping("/images-sse")
fun getImagesAsSSE(
request: HttpServletRequest
): Flux<ServerSentEvent<MutableList<Image>>> {
val subdomain = request.serverName.split(".").first()
return Flux.interval(Duration.ofSeconds(5))
.map {
ServerSentEvent.builder<MutableList<Image>>()
.event("periodic-event")
.data(weddingService.getBySubdomain(subdomain)?.pictures).build()
}
}
Example code for controller:
package sk.qpp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;
import java.util.concurrent.atomic.AtomicLong;
#Controller
#Slf4j
public class ReactiveController {
record SomeDTO(String name, String address) {
}
private final Sinks.Many<SomeDTO> eventSink = Sinks.many().multicast().directBestEffort();
#RequestMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<SomeDTO>> sse() {
final AtomicLong counter = new AtomicLong(0);
return eventSink.asFlux()
.map(e -> ServerSentEvent.builder(e)
.id(counter.incrementAndGet() + "")
//.event(e.getClass().getName())
.build());
}
// note, when you want this to work in production, ensure, that http request is not being cached on its way, using POST method for example.
#ResponseStatus(HttpStatus.OK)
#ResponseBody
#GetMapping(path = "/sendSomething", produces = MediaType.TEXT_PLAIN_VALUE)
public String sendSomething() {
this.eventSink.emitNext(
new SomeDTO("name", "address"),
(signalType, emitResult) -> {
log.warn("Some event is being not send to all subscribers. It will vanish...");
// returning false, to not retry emitting given data again.
return false;
}
);
return "Have a look at /sse endpoint (using \"curl http://localhost/sse\" for example), to see events in realtime.";
}
}
Sink is used as some "custom flux", where you can put anything (using emitNext), and take from it (using asFlux() method).
After setting up sample controller, open http://localhost:9091/sendSomething in your browser (i.e. do GET request on it) and in console issue command curl http://localhost:9091/sse to see your sse events (after each get request, new should come). It is possible also to see sse events directly in chromium browser. Firefox does try to download and save it to filesystem as file (works also).
I finally got it working. I also added user specific updates with a cookie.
Here is my SSE Controller
#RestController
#RequestMapping("/api/sse")
class SSEController {
val imageUpdateSink : Sinks.Many<Wedding> = Sinks.many().multicast().directBestEffort()
#GetMapping("/images")
fun getImagesAsSSE(
request: HttpServletRequest
): Flux<ServerSentEvent<MutableList<Image>>> {
val counter: AtomicLong = AtomicLong(0)
return imageUpdateSink.asFlux()
.filter { wedding ->
val folderId = request.cookies.find {cookie ->
cookie.name == "folderId"
}?.value
folderId == wedding.folderId
}.map { wedding ->
ServerSentEvent.builder<MutableList<Image>>()
.event("new-image")
.data(
wedding.pictures
).id(counter.incrementAndGet().toString())
.build()
}
}
}
In my Service where my data is updated:
val updatedImageList = weddingRepository.findByFolderId(imageDTO.folderId)
sseController.imageUpdateSink.tryEmitNext(
updatedImageList
)
My Javascript looks like this:
document.cookie = "folderId=" + [[${wedding.folderId}]]
const evtSource = new EventSource("/api/sse/images")
evtSource.addEventListener("new-image", function(alpineContext){
return function (event) {
console.log(event.data)
alpineContext.images = JSON.parse(event.data)
};
}(this))
Related
I'm trying to use Flux to generate asynchronous server sent events using Flux.create. When my client connects the request eventually times out with no event ever received. I hard-coded in an event to be sent by the Flux.create just to see data flow, but still nothing received client side.
#GetMapping(path = "/stream", headers = "Accept=*/*", consumes = MediaType.ALL_VALUE, produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<PricingDTO>> getEventStream() {
final Flux<ServerSentEvent<PricingDTO>> flux = Flux.<ServerSentEvent<PricingDTO>>create(emitter -> {
final PricingDTO pricing = new PricingDTO();
pricing.setId(99L);
emitter.next(ServerSentEvent.builder(pricing).build());
});
return flux;
}
Client side (Angular) code:
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
console.debug('Received event: ' + event);
const json = JSON.parse(event.data);
// Should be PricingDTO record here
};
eventSource.onerror = (error) => {
if (eventSource.readyState === EventSource.CLOSED) {
console.log('The stream has been closed by the server.');
eventSource.close();
} else {
console.log('Error here: ' + error);
}
};
I never see an event come through the EventSource. Eventually the request times out and I see the error: net::ERR_EMPTY_RESPONSE
I'm new to using WebFlux and I suspect I'm missing some initialization on the FluxStream before I return the Flux result. I have debugged and do see the request being received by my web service and the Flux object being returned. Any idea why I'm not receiving my events?
Your webflux code seems fine. I tested this with the following simplified example (without your custom classes).
#SpringBootApplication
#RestController
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class, args);
}
#GetMapping(path = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> getEventStream() {
return Flux.create(emitter -> emitter.next("hi").next("hi2"));
}
}
When connecting to the steam in chrome you get to see the events coming in just fine:
data:hi
data:hi2
the problem either lies in your accept header filter, or on the client side. You could ofcourse validate this by connecting to your stream in a browser (or better, a test)
I want to develop a protocol-buffer type RESTful API that replaces JSON with protocol-buffer.
For example, a Spring Boot implement:
#RestController
#RequestMapping("account")
public class AccountController {
#RequestMapping(
path = "ListAccount",
method = RequestMethod.POST,
consumes = {"application/x-protobuf", "application/x-protobuf;charset=UTF-8"},
produces = {"application/x-protobuf", "application/x-protobuf;charset=UTF-8"})
public ListAccountResponse listAccount(ListAccountRequest request) {
// do something
ListAccountResponse response = ListAccountResponse.newBuilder().build();
return response;
}
}
syntax = "proto3";
option java_multiple_files = true;
option java_package = "account.proto.api";
package account.api;
import "common/pagination.proto";
import "common/valid.proto";
import "account/data/account.proto";
message ListAccountRequest {
common.Pagination page = 1 [(valid.validate) = true];
}
message ListAccountResponse {
repeated account.data.Account accounts = 1;
common.Pagination page = 2;
}
It seems that no a GUI client like postman for testing protocol-buffer API, which make me writing a tester in java. I will appreciate it if someone can tell me the answer.
You can use BloomRpc.First of all you should import your proto files and after that change format of request according to your rpc service.
you can use protoman A Postman-like API client for protobuf-based messages.
https://github.com/spluxx/Protoman
I'm trying to modify an instance's tags list using the goolge compute engine API for Java. My pom.xml imports this dependency:
<dependency>
<groupId>com.google.apis</groupId>
<artifactId>google-api-services-compute</artifactId>
<version>v1-rev173-1.23.0</version>
</dependency>
I can execute the action that will update the tags associated with a VM successfully:
public boolean setInstanceTags(Compute computeConnection, ArrayList<String> nwTags, String projectId, String zone, String instanceName) {
Instance instance = computeConnection.instances().get(projectId, zone, instanceName).execute();
Tags tagsToSet = new Tags();
tagsToSet.setFingerprint(instance.getTags().getFingerprint());
tagsToSet.setItems(new ArrayList<String>());
for (String tag: nwTags) {
tagsToSet.getItems().add(tag);
}
Operation setTagsOperation = computeConnection.instances().setTags(projectId, zone, instanceName, tagsToSet).execute();
In order to get feedback on whether that operation succeeded I would like to pull the operation status as follows:
String setTagsOperationId = setTagsOperation.getName();
setTagsOperation = computeConnection.globalOperations().get(projectId, setTagsOperationId).execute();
This throws this error:
"code" : 403,
"errors" : [ {
"domain" : "global",
"message" : "Required 'compute.globalOperations.get' permission for 'projects/myproject/global/operations/operation-1523604756600-569b5e04b94c3-a87939f4-4e293939'",
"reason" : "forbidden"
} ],
"message" : "Required 'compute.globalOperations.get' permission for 'projects/myproject/global/operations/operation-1523604756600-569b5e04b94c3-a87939f4-4e293939'"
But the service account I'm using does have the "Compute Admin" IAM role and my code is also setting the admin scope:
SCOPES = Arrays.asList(ComputeScopes.COMPUTE);
I'm using the same account/permissions to create firewall rules and pull the status on those operations successfully. Not sure why there is a difference in permissions for pulling operation status for instances.setTags operations and firewalls.insert.
The only hint I found is when pulling data on the firewalls.insert operation the 'selfLink' shows that the operation is located in the global scope:
"https://www.googleapis.com/compute/v1/projects/myproject/global/operations/operation-1523604247193-569b5c1eea5a8-2ccf40e9-8815af38"
where as the instances.setTags operation selfLink shows that this operation is located in a specific zone:
"https://www.googleapis.com/compute/v1/projects/myproject/zones/us-central1-c/operations/operation-1523604346365-569b5c7d7e449-dc64de03-fdb77847"
You can modify the tag of an instance from console and check that this operation is a zonal operation, not a global operation. Below selfLink can be found from Equivalent REST response from any modify tag operation.
"selfLink": "https://www.googleapis.com/compute/v1/projects/[project_name]/zones/us-central1-f/operations/operation-0000000000000-0000000000000-00000000-00000000",
I believe for this reason, it is needed to use a zonal method rather than global method. You can use the below method to resolve this issue.
Method: zoneOperations.get
That said, Compute Admin role already has all required permissions including compute.zoneOperations permission to work in this case.
I have tested the following API [1] call with a service account with role "Compute Admin":
$ curl -H"Authorization: Bearer "$(gcloud auth print-access-token) https://www.googleapis.com/compute/v1/projects/[PROJECT_ID]/global/operations/[OPERATION_NAME]
And it returned me the expected value with no error.
In order to know if your code and service account have the right role, you can try the following code extracted from the official documentation [2] with the value set to "compute.globalOperations.get".
import com.google.api.client.googleapis.auth.oauth2.GoogleCredential;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.services.cloudresourcemanager.CloudResourceManager;
import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsRequest;
import com.google.api.services.cloudresourcemanager.model.TestIamPermissionsResponse;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Arrays;
public class CloudResourceManagerExample {
public static void main(String args[]) throws IOException, GeneralSecurityException {
// REQUIRED: The resource for which the policy detail is being requested.
// See the operation documentation for the appropriate value for this field.
String resource = "my-resource"; // TODO: Update placeholder value.
// TODO: Assign values to desired fields of `requestBody`:
TestIamPermissionsRequest requestBody = new TestIamPermissionsRequest();
CloudResourceManager cloudResourceManagerService = createCloudResourceManagerService();
CloudResourceManager.Projects.TestIamPermissions request =
cloudResourceManagerService.projects().testIamPermissions(resource, requestBody);
TestIamPermissionsResponse response = request.execute();
// TODO: Change code below to process the `response` object:
System.out.println(response);
}
public static CloudResourceManager createCloudResourceManagerService()
throws IOException, GeneralSecurityException {
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
GoogleCredential credential = GoogleCredential.getApplicationDefault();
if (credential.createScopedRequired()) {
credential =
credential.createScoped(Arrays.asList("https://www.googleapis.com/auth/cloud-platform"));
}
return new CloudResourceManager.Builder(httpTransport, jsonFactory, credential)
.setApplicationName("Google-CloudResourceManagerSample/0.1")
.build();
}
}
This way you will know if your code is using the correct service account or if there is an issue.
[1] https://cloud.google.com/compute/docs/reference/rest/beta/globalOperations/get
[2] https://cloud.google.com/resource-manager/reference/rest/v1/projects/testIamPermissions
Below is a script that helps me build an extentreport for jmeter. It is a JSR223 PostProcessor element. It's working nicely however, the problem is that I have it duplicated after every HTTP Request in the script. I have several scripts with 100's of HTTP requests that would need essentially a copy of the same PostProcessor groovy script. This = hard to maintain!
I have tried splitting common parts into an external groovy script that I tried calling on the JSR223 PostProcessor. I also tried chunking up the bits of the script and putting the values into a csv so that I could just update the csv values if anything changed.
I'm sure there's a cleaner/better way to do this but I'm still learning so I'm not sure of the best way to make this easier to maintain. Here's the JSR223 PostProcessor. The only bit that changes with each http request is the "//test result" section
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
//configure object for response data
def response = prev.getResponseDataAsString();
//configure extentreports objects
ExtentReports report;
ExtentTest testLogger;
//set location for file and report config
String resultsPath = "C:/portalQA/Automation/Results/";
String configPath = "C:/portalQA/Automation/JMeter/TestReportConfig/";
String reportPath =
resultsPath+"Login_Results_${reportDate}_${currentTime}_${testenv}.html";
File file = new File(reportPath);
if (!file.exists()) {
//if file does not exist, create it
report = new ExtentReports(reportPath, true);
report.loadConfig( new File(configPath+"extent-config.xml"));
} else {
//else append to existing report
report = new ExtentReports(reportPath, false);
report.loadConfig( new File(configPath+"extent-config.xml"));
}
//test result
testLogger = report.startTest("Authenticate");
testLogger.assignCategory("Initialize Session");
if (response.contains("com.blah.portal.model.User")) {
testLogger.log(LogStatus.PASS, "Logged in with: ${username}");
testLogger.log(LogStatus.INFO, response);
} else {
testLogger.log(LogStatus.FAIL, "Could not authenticate session");
testLogger.log(LogStatus.INFO, response);
}
log.info("Authenticate");
print("Authenticate print");
report.endTest(testLogger);
report.flush();
I see two options:
I suggest using JSR223 Listener instead. First of all, that way you will only have 1 listener in your script, which resolves your original problem, but it is a better option for writing into file in general, since listener has only one instance for all running threads, so you won't be creating a race condition when writing to file.
If you rather have a post-processor, you can put it on higher level (not under any particular sampler) which will cause it to run after each request within the same scope or below.
For example, configuration like
Thread Group
Post-processor
Sampler 1
...
Sampler N
Will cause Post-processor to run after each Sampler 1...Sampler N
In both cases you may need to check which sampler you are processing, and skip those you don't want to add to your report (easiest way to do it, is to come up with some name convention for excluded samplers)
I also faced the same challenge. In my case I need to check if JSON response from REST service was correct. I solved it in the following way.
I've created a JSR223 PreProcessor under the script root. It contains my custom class to handle JSON parsing and asserts.
import groovy.json.JsonSlurper
import org.apache.jmeter.assertions.AssertionResult
class CustomAssert {
def parseResponse(json) {
def jsonSlurper = new JsonSlurper()
return jsonSlurper.parseText(json)
}
def assertResult(assertionResult, expectedResult, actualResult) {
if (!expectedResult.equals(actualResult)) {
assertionResult.setFailure(true);
assertionResult.setFailureMessage("Expected ${expectedResult} but was ${actualResult}");
}
}
}
vars.putObject('customAssert', new CustomAssert())
Note the last line:
vars.putObject('customAssert', new CustomAssert())
I put an instance of my CustomAssert to vars.
Then under my HTTP Requests I've added JSR233 Assertion
def a = vars.getObject('customAssert')
def response = a.parseResponse(prev.getResponseDataAsString())
a.assertResult(AssertionResult, 'DRY', response.sensorResultHolderUIs[0].result.toString())
a.assertResult(AssertionResult, 'DRY', response.sensorResultHolderUIs[1].result.toString())
a.assertResult(AssertionResult, 'DRY', response.sensorResultHolderUIs[2].result.toString())
It basically retrieves the instance of CustomAssert from vars and calls its methods. I can put as many JSR233 Assertions as I want. The only code that is copied is those two lines on top:
def a = vars.getObject('customAssert')
def response = a.parseResponse(prev.getResponseDataAsString())
To sum up:
Take the common part of your code (that doesn't have to be copied).
Wrap it in a class.
Put the class in JSR233 PreProcessor under the root and export its instance via vars
Take the rest of your code and adjust it to use class defined in 2.
Put that code in as many JSR233 Assertions as you want remembering to retrieve the instance created in 3. from vars
Thank you user1053510. Your advice lead me to build my own JSR223 Listener that renders the report. Below is the code in my JSR223 Listener:
import com.aventstack.extentreports.*;
import com.aventstack.extentreports.reporter.*;
import com.aventstack.extentreports.markuputils.*;
ExtentHtmlReporter htmlReporter;
ExtentReports extent;
ExtentTest test;
// create the HtmlReporter
htmlReporter = new ExtentHtmlReporter("C:/AUTO_Results/Results_${testApp}_${reportDate}_${currentTime}_${testenv}.html");
//configure report
htmlReporter.config().setCreateOfflineReport(true);
htmlReporter.config().setChartVisibilityOnOpen(true);
htmlReporter.config().setDocumentTitle("${testApp} Results");
htmlReporter.config().setEncoding("utf-8");
htmlReporter.config().setReportName("${testApp} Results ${reportDate}_${currentTime}_${testenv}");
htmlReporter.setAppendExisting(true);
// create ExtentReports
extent = new ExtentReports();
// attach reporter to ExtentReports
extent.attachReporter(htmlReporter);
extent.setReportUsesManualConfiguration(true);
// Show Env section and set data on dashboard
extent.setSystemInfo("Tool","JMeter");
extent.setSystemInfo("Test Env","${testenv}");
extent.setSystemInfo("Test Date","${reportDate}");
extent.setSystemInfo("Test Time","${currentTime}");
//stringify test info
String threadName = sampler.getThreadName();
String samplerName = sampler.getName();
String requestData = props.get("propRequestData");
String respCode = props.get("propRespCode");
String respMessage = props.get("propRespMessage");
String responseData = props.get("propResponse");
// create test
test = extent.createTest(threadName+" - "+samplerName);
//test.assignCategory("API Testing");
// analyze sampler result
if (vars.get("JMeterThread.last_sample_ok") == "false") {
log.error("FAILED: "+samplerName);
print("FAILED: "+samplerName);
test.fail(MarkupHelper.createLabel("FAILED: "+sampler.getName(),ExtentColor.RED));
} else if (vars.get("JMeterThread.last_sample_ok") == "true") {
if(responseData.contains("#error")) {
log.info("FAILED: "+sampler.getName());
print("FAILED: "+sampler.getName());
test.fail(MarkupHelper.createLabel("FAILED: "+sampler.getName(),ExtentColor.RED));
} else if (responseData.contains("{")) {
log.info("Passed: "+sampler.getName());
print("Passed: "+sampler.getName());
test.pass(MarkupHelper.createLabel("Passed: "+sampler.getName(),ExtentColor.GREEN));
}
} else {
log.error("Something is really wonky");
print("Something is really wonky");
test.fatal("Something is really wonky");
}
//info messages
test.info("RequestData: "+requestData);
test.info("Response Code and Message: "+respCode+" "+respMessage);
test.info("ResponseData: "+responseData);
//playing around
//markupify json into code blocks
//Markup m = MarkupHelper.createCodeBlock(requestData);
//test.info(MarkupHelper.createModal("Modal text"));
//Markup mCard = MarkupHelper.createCard(requestData, ExtentColor.CYAN);
// test.info("Request "+m);
// test.info(mCard);
// test.info("Response Data: "+MarkupHelper.createCodeBlock(props.get("propResponse")));
// test.info("ASSERTION MESSAGE: "+props.get("propAssertion"));
// end the reporting and save the file
extent.flush();
Then in each threadgroup I have a BeanShell Assertion with these lines:
//request data
String requestData = new String(prev.SamplerData);
//String requestData = new String(requestData);
props.put("propRequestData", requestData);
//response data
String respData = new String(prev.ResponseData);
//String respData = new String(prev.getResponseDataAsString());
props.put("propResponse", respData);
//response code
String respCode = new String(prev.ResponseCode);
props.put("propRespCode",respCode);
//response message
String respMessage = new String(prev.ResponseMessage);
props.put("propRespMessage",respMessage);
I am creating a jmeter scritp and the scnerio where i am struck is as follows:
First Try login to an application with Password 1, if login fails then try with password 2, if login fails with password2 aswell then the result should be fail otherwise pass.
I have created a HTTP request and Password1, password2 are user defined variables.
Have two requests Req_1 and Req_2 and configure them with 'login' action one with each password. Use 'Regular Expression Extractor' or other to parse the response to Req_1 and use 'If Controller' to see if it passed or not and submit Req_2 only if Req_1 login fails.
Add BeanShell Sampler to the Test Plan and add the following code (working code for the given website):
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.apache.http.NameValuePair;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import java.util.List;
import java.util.ArrayList;
HttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://httpbin.org/post");
List nvps = new ArrayList();
nvps.add(new BasicNameValuePair("username", "vip"));
nvps.add(new BasicNameValuePair("password", "secret"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps));
HttpResponse response = httpclient.execute(httpPost);
String responseString = EntityUtils.toString(response.getEntity());
String responseCode = String.valueOf(response.getStatusLine().getStatusCode());
String errorMessage = "secret";
if(responseString.contains(errorMessage)){
log.info("first time failed");
HttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://httpbin.org/post");
List nvps = new ArrayList();
nvps.add(new BasicNameValuePair("username", "vip"));
nvps.add(new BasicNameValuePair("password", "secret"));
httpPost.setEntity(new UrlEncodedFormEntity(nvps));
HttpResponse response = httpclient.execute(httpPost);
String responseString = EntityUtils.toString(response.getEntity());
String responseCode = String.valueOf(response.getStatusLine().getStatusCode());
if(responseString.contains(errorMessage)){
log.info("second time failed");
IsSuccess = false;
ResponseCode = "403";
ResponseMessage = "Login failed with both the passwords";
}
}
return responseString;
Add above script to "Script" text area in the BeanShell sampler.
Image reference:
Note: replace the URL, username, password and error message as per your requirements. Add any headers if crucial.
If you want to use HTTP Sampler, follow the process as suggested by #SaiMatam.
Add If controller, compare whether the error message is received (write condition in Condition field).
Keep the same sampler (change only the password) inside If Controller. this sampler would get execute only if the condition is satisfied.
Add Response Assertion for the sampler in If Controller. Add the error message in "Pattern to test" field. select "Not" checkbox in "Pattern Matching Rules". This will make sure that, if the error message is received, the second request is marked as FAIL by JMeter (we are checking that the response should not contain the given string/pattern), otherwise will mark it as PASS.
References:
http://jmeter.apache.org/usermanual/component_reference.html#If_Controller
http://jmeter.apache.org/usermanual/component_reference.html#Response_Assertion