suspend function testing with spock - kotlin

I have a simple function in kotlin like that :
suspend fun createTicket(#Valid request: CreateTicketRequest, authentication: Authentication): HttpResponse<Any> {
request.customerId = "customerId"
logger().info("Receive by the client $request")
return HttpResponse.created(service.create(request))
}
I've already Mock the request and the authentication.
So, I call it on Spock:
def 'It should create a ticket with success'() {
given:
def request = createRequest(
TICKET_ID,
TICKET_NAME,
TICKET_PHONE,
TICKET_CPF,
TICKET_EMAIL,
TICKET_COMMENT,
TICKET_SUBJECT,
TICKET_TAG
)
when:
response = controller.createTicket(
request,
authentication
)
then:
response != null
}
I'm getting the following error :
Suspend function 'create' should be called only from a coroutine or another suspend function.
Can anyone help me with this question ?
Best regards

Solved I created a Kotlin class code
class CallCreateTicket {
private lateinit var response: HttpResponse<Any>
private fun createTicket(
request: CreateTicketRequest,
authenticator: Authenticator,
controller: TicketController,
): HttpResponse<Any> {
runBlocking {
response = controller.createTicket(request, authenticator)
}
return response
}
}
and I called it on groovy ...
#Mockable(TicketCreateServiceImpl)
class TicketControllerTest extends Specification {
def mockUtil = new MockUtil()
def service = Mock(TicketCreateServiceImpl)
def authenticator = Mock(Authenticator)
def response = Mock(HttpResponse)
def controller = new TicketController(service)
def callCreateTicket = new CallCreateTicket()
def 'check if all instances are mocked'() {
mockUtil.isMock(authentication)
mockUtil.isMock(service)
}
def 'It should call the index function and return a valid String'() {
when:
response = controller.index()
then:
response == INDEX_RETURN
}
def 'It should call the index function and return a invalid String'() {
when:
response = controller.index()
then:
response != INVALID_INDEX_RETURN
}
def 'It should create a ticket with success'() {
given:
def request = createRequest(
TICKET_ID,
TICKET_NAME,
TICKET_PHONE,
TICKET_CPF,
TICKET_EMAIL,
TICKET_COMMENT,
TICKET_SUBJECT,
TICKET_TAG
)
when:
response = callCreateTicket.createTicket(
request,
authenticator,
controller
)
then:
response.status(HttpStatus.CREATED)
}
}

Related

How to return bad request in spring webflux when there is an error?

I have this server endpoint using spring-webflux and I would like to return ServerResponse.badRequest() when the serverRequest receives a wrong parameter. The request curl -s "http://localhost:8080/4?a=5&b=3"; echo for instance, contains the right parameters. But the request curl -s "http://localhost:8080/one?a=5&b=3"; echo contains a string instead of an Integer. Then the conversion new BidRequest(Integer.parseInt(tuple2.getT1()), tuple2.getT2().toSingleValueMap()) will throw an error.
I was doing .onErrorReturn(new BidRequest(0, null)) but now I want to implement some operation that return ServerResponse.badRequest(). So I added in the end .onErrorResume(error -> ServerResponse.badRequest().build()) in the end, but It is not working. I also added on the place of the code .onErrorReturn() and it does not compile.
public Mono<ServerResponse> bidRequest(ServerRequest serverRequest) {
var adId = serverRequest.pathVariable("id");
var attributes = serverRequest.queryParams();
log.info("received bid request with adID: {} attributes: {}", adId, attributes);
return Mono.just(Tuples.of(adId, attributes))
.map(tuple2 -> new BidRequest(Integer.parseInt(tuple2.getT1()), tuple2.getT2().toSingleValueMap()))
// I WANT TO REPLACE IT FOR A BAD REQUEST
// .onErrorReturn(new BidRequest(0, null))
.flatMap(bidRequest -> {
return Flux.fromStream(bidderService.bidResponseStream(bidRequest))
.flatMap(this::gatherResponses)
.reduce((bidResp1, bidResp2) -> {
if (bidResp1.getBid() > bidResp2.getBid()) return bidResp1;
else return bidResp2;
});
})
.map(bid -> {
var price = bid.getContent().replace("$price$", bid.getBid().toString());
bid.setContent(price);
return bid;
})
.flatMap(winner -> {
return ServerResponse.ok()
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(winner.getContent()));
})
.switchIfEmpty(ServerResponse.notFound().build())
// THIS DOES NOT RETURN ANY BAD REQUEST
.onErrorResume(error -> ServerResponse.badRequest().build());
}
I solved based on this answer using flatmap and returning a Mono.just() or a Mono.error(new ResponseStatusException(HttpStatus.BAD_REQUEST));.
return Mono
.just(Tuples.of(adId, attributes))
.flatMap(tuple2 -> {
if (validate(tuple2)) {
log.info("request parameters valid: {}", tuple2);
return Mono.just(new BidRequest(Integer.parseInt(tuple2.getT1()), tuple2.getT2().toSingleValueMap()));
} else {
log.error("request parameters invalid: {}", tuple2);
return Mono.error(new ResponseStatusException(HttpStatus.BAD_REQUEST));
}
})
.flatMap(....
private boolean validate(Tuple2<String, MultiValueMap<String, String>> tuple2) {
return GenericValidator.isInteger(tuple2.getT1());
}

Debug/Log request for akka-http server

I have the following test http, and I would like to get all the information from the request in the default route.
class HttpTestServer(port: Int = 6666)(implicit as: ActorSystem, ec: ExecutionContext, mat: ActorMaterializer)
extends JsonSupport
with LazyLogging{
var numRequest: Int = 0
def start(): Future[ServerBinding] = {
def routes = {
// print request information here
logRequest("test")
numRequest += 1
println(s"HttpServer request received: $numRequest")
complete(Map("key"-> 1.0))
}
Http().bindAndHandle(routes, "0.0.0.0", port)
}
}
And I have the following line at logback.xml, to enable the debug at akka-http:
<logger name="akka.http" level="DEBUG"/>
As a references, I am using akka-http DebuggingDirective
UPDATED: My guess is that there is a problem with the LoggingAdapter or incompatibility between LazyLogging and the akka LoggingAdapter. If I print to stdout works fine:
val logRequestPrintln = DebuggingDirectives.logRequest(LoggingMagnet(_ => println _))
....
logRequestPrintln(complete(Map("key"-> 1.0)))

play2-auth don't recognize user after login

I'm trying to implement a login with play2-auth and playframework 2.2.0, I implemented the logic from the last branch using async, after a success login the Home action don't recognize the logged user and redirect to login again, some code:
The AuthConfig trait:
trait AuthConfigImpl extends AuthConfig {
type Id = String
type User = Account
type Authority = models.poso.Permission
val idTag: ClassTag[Id] = classTag[Id]
val sessionTimeoutInSeconds: Int = 3600
def resolveUser(id: Id)(implicit ctx: ExecutionContext): Future[Option[User]] = Future.successful(Cache.getAccountJson(id))
def loginSucceeded(request: RequestHeader)(implicit ctx: ExecutionContext): Future[SimpleResult] =
Future.successful(Redirect(routes.Home.test))
def logoutSucceeded(request: RequestHeader)(implicit ctx: ExecutionContext): Future[SimpleResult] =
Future.successful(Redirect(routes.Login2.login))
def authenticationFailed(request: RequestHeader)(implicit ctx: ExecutionContext): Future[SimpleResult] =
Future.successful(Redirect(routes.Login2.login))
def authorizationFailed(request: RequestHeader)(implicit ctx: ExecutionContext): Future[SimpleResult] =
Future.successful(Forbidden("no permission"))
def authorize(user: User, authority: Authority, request: RequestHeader)(implicit ctx: ExecutionContext): Future[Boolean] = Future.successful {
val perm = user.user.permissao.tree.get(request.path).getOrElse(false)
perm match {
case true => true
case _ => false
}
}
}
The Home action:
class Home #Inject() (implicit sessionService: SessionService) extends Controller with AuthElement with AuthConfigImpl {
def test() = StackAction(AuthorityKey -> NormalUser) { implicit request =>
{
val u = loggedIn
//usurio tem permissao
Ok(views.html.home(u.user.email.toString))
// Ok(views.html.home(user.get.email.toString))
}
}
}
The login class:
class Login2 #Inject() (implicit sessionService: SessionService, loginService: LoginService, userService: UserService) extends Controller with LoginLogout with AuthConfigImpl with Logging {
def authenticate = Action.async { implicit request =>
{
val form = userForm.bindFromRequest
try {
form.fold(
errors => {
Future.successful(BadRequest(views.html.login("", userForm)))
},
other1Form => {
val login = loginService.loginVerify(other1Form.email, other1Form.password)
val uuidGenerate = java.util.UUID.randomUUID.toString
val account = userService.getDataFromUser(other1Form.email)
Cache.addEntry(EhCacheRegion.Cerberus.toString() + SessionProductName.Account.toString(), uuidGenerate, models.poso.Session.toJson(account))
gotoLoginSucceeded(uuidGenerate)
})
} catch {
case login: LoginException => {
val formError = form.withGlobalError(login.msg)
Future.successful(BadRequest(views.html.login("", formError)))
}
case ex: Exception => {
logger.error(ex.getMessage())
Future.successful(BadRequest(views.html.login("", form.withGlobalError("system error"))))
}
case _: Any => {
logger.error("error")
Future.successful(BadRequest("err"))
}
}
}
}
}
this is the result on browser:
Make sure that you don't have secure cookies set, and try to test in an unsecured environment.
Check AuthConfig, for lazy val cookieSecureOption

how to access session in integration test in grails?

In my project, i set session.loggedInUser in login controller. But during integration test , we dont use login controller. So i have set value for session.loggedInUser. But i couldn't use session in that place. How can i use session in integration Test. Give some solution for this. thank you in advance
class MaritalStatusIntegrationTests {
#Test
void testCategoryAudit() {
RequestContextHolder.currentRequestAttributes().session.loggedInUser="Anantha"
def category = new Category(name:"Single")
category.save(flush:true)
assert CategoryAudit.count() == 1
category.name="Married"
category.save(flush:true)
assert CategoryAudit.count() == 2
}
}
Category.groovy:
class Category {
static constraints = {
name blank:false
}
String name
//Auditing
static auditable = false
def onSave = {
new CategoryAudit(this,'Insert').save(failOnError:true)
}
}
CategoryAudit.groovy:
import org.springframework.web.context.request.RequestContextHolder
class CategoryAudit {
String name
String operation
String doneBy
Date txnDate
def CategoryAudit(){}
def CategoryAudit(Category category , String operation) {
this.name = category.name
this.operation = operation
this.doneBy = RequestContextHolder.currentRequestAttributes().session.loggedInUser
this.txnDate = new Date()
}
}
No such property: RequestContextHolder for class:
com.vasco.gs.MaritalStatusIntegrationTest.
Just to clean up, according to the OP, it was missing the import for RequestContextHolder.

Struts2 more than one action in one class

I'm using Struts2. I have two web forms that have the same code. I would like to eliminate one form. Here is the structure of my Struts project.
\Web Pages
form.jsp
\WEB-INF
\Content
error.jsp
form.jsp
success.jsp
\Source Packages
\action
MyAction.java
MyAction.java
package action;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.*;
public class MyAction extends ActionSupport {
#Action(value = "foo", results = {
#Result(name = "input", location = "form.jsp"),
#Result(name = "success", location = "success.jsp"),
#Result(name = "error", location = "error.jsp")
})
public String execute() throws Exception {
if (user.length() == 1) {
return "success";
} else {
return "error";
}
}
private String user = "";
public void validate() {
if (user.length() == 0) {
addFieldError("user", getText("user required"));
}
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
}
I tried to eliminate form.jsp under \Web Pages by adding a new action method to MyAction.java.
#Action(value="bar", results = {
#Result(name = "success", location = "form.jsp"),
})
public String another() {
return "success";
}
But I got the following error when I go to http : //localhost .../bar.action
HTTP Status 404 - No result defined for action action.MyAction and result input
Your MyAction has an implementation of validate(), which means it is validation aware.
What's happening is that you're calling another, but validate() is kicking in (as it's in the interceptor stack). Validation is failing, and therefore sending to INPUT result, which is not defined in another.
You should
Add #SkipValidation to the another method if you don't want validation there
Add the INPUT result to another() if you want a default input result
On a more general note, when you get that kind of error (No result defined for action X and result input) it usually means you're either having validation errors, parameter population errors (eg: an exception in preparable).