blob: 908277eb15e383e300449e7d86e6817bde10630d [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.nlpcraft.server.rest
import java.util
import java.util.UUID
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import com.jayway.jsonpath.{JsonPath, PathNotFoundException}
import org.apache.http.client.ResponseHandler
import org.apache.http.client.methods.HttpPost
import org.apache.http.entity.StringEntity
import org.apache.http.impl.client.HttpClients
import org.apache.http.util.EntityUtils
import org.apache.http.{HttpEntity, HttpResponse}
import org.apache.nlpcraft.NCTestContext
import org.junit.jupiter.api.Assertions._
import org.junit.jupiter.api.{AfterEach, BeforeEach}
import org.apache.nlpcraft.common._
import scala.collection.JavaConverters._
private[rest] object NCRestSpec {
private final val DFLT_BASEURL = "http://localhost:8081/api/v1/"
private final val DFLT_ADMIN_EMAIL = "admin@admin.com"
private final val DFLT_ADMIN_PSWD = "admin"
private final val TYPE_RESP = new TypeToken[util.Map[String, Object]]() {}.getType
private final val GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create()
private final val CLI = HttpClients.createDefault
private final val HANDLER: ResponseHandler[java.util.Map[String, Object]] =
(resp: HttpResponse){
val code = resp.getStatusLine.getStatusCode
val js = mkJs(code, resp.getEntity)
code match {
case 200 ⇒ GSON.fromJson(js, TYPE_RESP)
case 400throw new RuntimeException(js)
case _ ⇒ throw new RuntimeException(s"Unexpected response [code=$code, response=$js]")
}
}
private final val ERR_HANDLER: ResponseHandler[Error] =
(resp: HttpResponse){
val code = resp.getStatusLine.getStatusCode
Error(code, mkJs(code, resp.getEntity))
}
/**
*
* @param code
* @param e
* @return
*/
private def mkJs(code: Int, e: HttpEntity): String = {
if (e == null)
throw new RuntimeException(s"Unexpected empty response [code=$code]")
EntityUtils.toString(e)
}
/**
*
* @param url
* @param ps
*/
private def post0(url: String, ps: (String, Any)*): Map[String, Object] = {
val post = preparePost(url, ps: _*)
val m =
try
CLI.execute(post, HANDLER)
finally
post.releaseConnection()
m.asScala.toMap
}
/**
*
* @param url
* @param ps
* @return
*/
private def preparePost(url: String, ps: (String, Any)*) = {
val post = new HttpPost(DFLT_BASEURL + url)
post.setHeader("Content-Type", "application/json")
post.setEntity(new StringEntity(GSON.toJson(ps.filter(_._2 != null).toMap.asJava), "UTF-8"))
post
}
/**
*
* @param resp
*/
private def checkStatus(resp: Map[String, Object]): Unit = {
assertTrue(resp.contains("status"))
assertEquals("API_OK", resp("status"))
}
case class Error(httpCode: Int, js: String)
}
import org.apache.nlpcraft.server.rest.NCRestSpec._
private[rest] class NCRestSpec extends NCTestContext {
type ResponseContent = java.util.Map[String, Object]
type ResponseList = java.util.List[ResponseContent]
type JList[T] = java.util.List[T]
private var tkn: String = _
/**
*
*/
@BeforeEach
def signin(): Unit = tkn = signin(DFLT_ADMIN_EMAIL, DFLT_ADMIN_PSWD)
/**
*
* @param email
* @param passwd
*/
protected def signin(email: String, passwd: String): String = {
val tkn = post0("signin", "email" → email, "passwd" → passwd)("acsTok").asInstanceOf[String]
assertNotNull(tkn)
tkn
}
/**
*
*/
@AfterEach
def signout(): Unit =
if (tkn != null) {
signout(tkn)
tkn = null
}
/**
*
* @param tkn
*/
protected def signout(tkn: String): Unit = checkStatus(post0("signout", "acsTok" → tkn))
/**
*
* @param url
* @param ps
* @param validations
*/
protected def post[T](url: String, ps: (String, Any)*)(validations: (String, T ⇒ Unit)*): Unit = {
assertNotNull(tkn)
post(url, tkn, ps: _*)(validations: _*)
}
/**
*
* @param url
* @param tkn
* @param ps
* @param validations
*/
protected def post[T](url: String, tkn: String, ps: (String, Any)*)(validations: (String, T ⇒ Unit)*): Unit = {
val resp = post0(url, addToken(tkn, ps): _*)
checkStatus(resp)
println("Checked POST:")
println(U.colorJson(
GSON.toJson(
Map(
"url" → url,
"params"new java.util.HashMap[String, Any](ps.toMap.asJava),
"response" → resp.asJava
).asJava
))
)
val ctx = JsonPath.parse(GSON.toJson(resp.asJava))
validations.foreach { case (field, validation)
val v: Object =
try
ctx.read(field)
catch {
case _: PathNotFoundExceptionnull
}
println(s"Validating value [$field=$v]")
validation(v match {
case arr: net.minidev.json.JSONArray(0 until arr.size()).map(i ⇒ arr.get(i)).asJava.asInstanceOf[T]
case _ ⇒ v.asInstanceOf[T]
})
}
println()
}
/**
*
* @param url
* @param httpErrCode
* @param errCode
* @param ps
*/
protected def postError(url: String, httpErrCode: Int, errCode: String, ps: (String, Any)*): Unit = {
assertNotNull(tkn)
val post = preparePost(url, addToken(tkn, ps): _*)
val err =
try
CLI.execute(post, ERR_HANDLER)
finally
post.releaseConnection()
println(s"Checked POST with expected error [httpErrCode=$httpErrCode, errorCode=$errCode]")
println(GSON.toJson(
Map(
"url" → url,
"params"new java.util.HashMap[String, Any](ps.toMap.asJava),
"response"Map("httpCode" → err.httpCode, "json" → GSON.fromJson(err.js, TYPE_RESP)).asJava
).asJava
))
println()
assertEquals(httpErrCode, err.httpCode)
val js = err.js
val m: java.util.Map[String, Object] = GSON.fromJson(js, TYPE_RESP)
assertEquals(
errCode,
m.asScala.getOrElse("code", ()throw new RuntimeException(s"Invalid response: $js")).asInstanceOf[String]
)
}
/**
*
* @param tkn
* @param ps
*/
private def addToken(tkn: String, ps: Seq[(String, Any)]): Seq[(String, Any)] =
if (ps.exists(_._1 == "acsTok")) ps else Seq("acsTok" → tkn) ++ ps
/**
*
* @param data
* @param field
* @param expected
*/
protected def containsLong(data: ResponseList, field: String, expected: Long): Boolean =
contains(data, field, (o: Object) ⇒ o.asInstanceOf[Number].longValue(), expected)
/**
*
* @param data
* @param field
* @param extract
* @param expected
*/
protected def contains[T](data: ResponseList, field: String, extract: Object ⇒ T, expected: T): Boolean =
data.asScala.exists(p ⇒ extract(p.get(field)) == expected)
/**
*
* @param data
* @param field
* @param expected
*/
protected def containsStr(data: ResponseList, field: String, expected: String): Boolean =
contains(data, field, (o: Object) ⇒ o.asInstanceOf[String], expected)
/**
*
* @param data
* @param field
* @param extract
* @param expected
*/
protected def count[T](data: ResponseList, field: String, extract: Object ⇒ T, expected: T): Int =
data.asScala.count(p ⇒ extract(p.get(field)) == expected)
/**
*
*/
protected def rnd(): String = UUID.randomUUID().toString
/**
*
* @param n
* @return
*/
protected def mkString(n: Int, ch: Char = '*'): String = (0 to n).map(_ ⇒ ch).mkString
}