nim-presto/tests/testserver.nim

938 lines
34 KiB
Nim

import std/[strutils, algorithm]
import chronos, chronos/apps, chronos/unittest2/asynctests, stew/byteutils,
metrics
import helpers, ../presto/route, ../presto/segpath, ../presto/server
when defined(nimHasUsed): {.used.}
type
ClientResponse = object
status*: int
data*: string
headers*: HttpTable
proc cmpNoHeaders(a, b: ClientResponse): bool =
(a.status == b.status) and (a.data == b.data)
proc cmpWithHeaders(a, b: ClientResponse): bool =
if (a.status != b.status) or (a.data != b.data):
return false
for header in b.headers.items():
if header.key notin a.headers:
return false
let checkItems = a.headers.getList(header.key).sorted()
let expectItems = header.value.sorted()
if checkItems != expectItems:
return false
true
proc cmpNoContent(a, b: ClientResponse): bool =
if (a.status != b.status):
return false
for header in b.headers.items():
if header.key notin a.headers:
return false
let checkItems = a.headers.getList(header.key).sorted()
let expectItems = header.value.sorted()
if checkItems != expectItems:
return false
true
proc init(t: typedesc[ClientResponse], status: int): ClientResponse =
ClientResponse(status: status)
proc init(t: typedesc[ClientResponse], status: int,
headers: openArray[tuple[key, value: string]]): ClientResponse =
let table = HttpTable.init(headers)
ClientResponse(status: status, headers: table)
proc init(t: typedesc[ClientResponse], status: int,
data: string): ClientResponse =
ClientResponse(status: status, data: data)
proc init(t: typedesc[ClientResponse], status: int, data: string,
headers: HttpTable): ClientResponse =
ClientResponse(status: status, data: data, headers: headers)
proc init(t: typedesc[ClientResponse], status: int, data: string,
headers: openArray[tuple[key, value: string]]): ClientResponse =
let table = HttpTable.init(headers)
ClientResponse(status: status, data: data, headers: table)
proc httpClient(server: TransportAddress, meth: HttpMethod, url: string,
body: string, ctype = "",
accept = ""): Future[ClientResponse] {.async.} =
var request = $meth & " " & $parseUri(url) & " HTTP/1.1\r\n"
request.add("Host: " & $server & "\r\n")
request.add("Content-Length: " & $len(body) & "\r\n")
if len(ctype) > 0:
request.add("Content-Type: " & ctype & "\r\n")
if len(accept) > 0:
request.add("Accept: " & accept & "\r\n")
request.add("\r\n")
if len(body) > 0:
request.add(body)
var headersBuf = newSeq[byte](4096)
let transp = await connect(server)
let wres {.used.} = await transp.write(request)
let rlen = await transp.readUntil(addr headersBuf[0], len(headersBuf),
HeadersMark)
headersBuf.setLen(rlen)
let resp = parseResponse(headersBuf, true)
doAssert(resp.success())
let headers =
block:
var res = HttpTable.init()
for key, value in resp.headers(headersBuf):
res.add(key, value)
res
let length = resp.contentLength()
doAssert(length >= 0)
let cresp =
if length > 0:
var dataBuf = newString(length)
await transp.readExactly(addr dataBuf[0], len(dataBuf))
ClientResponse.init(resp.code, dataBuf, headers)
else:
ClientResponse.init(resp.code, "", headers)
await transp.closeWait()
return cresp
template asyncTest*(name: string, body: untyped): untyped =
test name:
waitFor((
proc() {.async, gcsafe.} =
body
)())
suite "REST API server test suite":
let serverAddress = initTAddress("127.0.0.1:30180")
asyncTest "Responses test":
var router = RestRouter.init(testValidate)
router.api(MethodGet, "/test/simple/1") do () -> RestApiResponse:
discard
router.api(MethodGet, "/test/simple/2") do () -> RestApiResponse:
return RestApiResponse.response("ok-1")
router.api(MethodGet, "/test/simple/3") do () -> RestApiResponse:
return RestApiResponse.error(Http505, "Some error", "text/error")
router.api(MethodGet, "/test/simple/4") do () -> RestApiResponse:
if true:
raise newException(ValueError, "Some exception")
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
# Handler returned empty response.
let res1 = await httpClient(serverAddress, MethodGet, "/test/simple/1",
"")
# Handler returned good response.
let res2 = await httpClient(serverAddress, MethodGet, "/test/simple/2",
"")
# Handler returned via RestApiResponse.
let res3 = await httpClient(serverAddress, MethodGet, "/test/simple/3",
"")
# Exception generated by handler.
let res4 = await httpClient(serverAddress, MethodGet, "/test/simple/4",
"")
# Missing handler response
let res5 = await httpClient(serverAddress, MethodGet, "/test/simple/5",
"")
# URI with more than 64 segments response
let res6 = await httpClient(serverAddress, MethodGet,
"//////////////////////////////////////////" &
"//////////////////////////test", "")
check:
cmpNoHeaders(res1, ClientResponse.init(410))
cmpNoHeaders(res2, ClientResponse.init(200, "ok-1"))
cmpNoHeaders(res3, ClientResponse.init(505, "Some error"))
cmpNoHeaders(res4, ClientResponse.init(503))
cmpNoHeaders(res5, ClientResponse.init(404))
cmpNoHeaders(res6, ClientResponse.init(400))
finally:
await server.closeWait()
asyncTest "Requests [path] arguments test":
var router = RestRouter.init(testValidate)
router.api(MethodGet, "/test/{smp1}") do (
smp1: int) -> RestApiResponse:
if smp1.isErr():
return RestApiResponse.error(Http411, $smp1.error())
return RestApiResponse.response($smp1.get())
router.api(MethodGet, "/test/{smp1}/{smp2}") do (
smp1: int, smp2: string) -> RestApiResponse:
if smp1.isErr():
return RestApiResponse.error(Http411, $smp1.error())
if smp2.isErr():
return RestApiResponse.error(Http412, $smp2.error())
return RestApiResponse.response($smp1.get() & ":" &
smp2.get())
router.api(MethodGet, "/test/{smp1}/{smp2}/{smp3}") do (
smp1: int, smp2: string, smp3: seq[byte]) -> RestApiResponse:
if smp1.isErr():
return RestApiResponse.error(Http411, $smp1.error())
if smp2.isErr():
return RestApiResponse.error(Http412, $smp2.error())
if smp3.isErr():
return RestApiResponse.error(Http413, $smp3.error())
return RestApiResponse.response($smp1.get() & ":" & smp2.get() & ":" &
toHex(smp3.get()))
const TestVectors = [
("/test/1234", ClientResponse.init(200, "1234")),
("/test/12345678", ClientResponse.init(200, "12345678")),
("/test/00000001", ClientResponse.init(200, "1")),
("/test/0000000", ClientResponse.init(200, "0")),
("/test/99999999999999999999999", ClientResponse.init(411)),
("/test/nondec", ClientResponse.init(404)),
("/test/1234/text1", ClientResponse.init(200, "1234:text1")),
("/test/12345678/texttext2",
ClientResponse.init(200, "12345678:texttext2")),
("/test/00000001/texttexttext3",
ClientResponse.init(200, "1:texttexttext3")),
("/test/0000000/texttexttexttext4",
ClientResponse.init(200, "0:texttexttexttext4")),
("/test/nondec/texttexttexttexttext5", ClientResponse.init(404)),
("/test/99999999999999999999999/texttexttexttexttext5",
ClientResponse.init(411)),
("/test/1234/text1/0xCAFE",
ClientResponse.init(200, "1234:text1:cafe")),
("/test/12345678/text2text2/0xdeadbeaf",
ClientResponse.init(200, "12345678:text2text2:deadbeaf")),
("/test/00000001/text3text3text3/0xabcdef012345",
ClientResponse.init(200, "1:text3text3text3:abcdef012345")),
("/test/00000000/text4text4text4text4/0xaa",
ClientResponse.init(200, "0:text4text4text4text4:aa")),
("/test/nondec/text5/0xbb", ClientResponse.init(404)),
("/test/99999999999999999999999/text6/0xcc", ClientResponse.init(411)),
("/test/1234/text7/0xxx", ClientResponse.init(413))
]
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
for item in TestVectors:
let res = await httpClient(serverAddress, MethodGet,
item[0], "")
check res.status == item[1].status
if len(item[1].data) > 0:
check res.data == item[1].data
finally:
await server.closeWait()
asyncTest "Requests [path + query] arguments test":
var router = RestRouter.init(testValidate)
router.api(MethodGet, "/test/{smp1}/{smp2}/{smp3}") do (
smp1: int, smp2: string, smp3: seq[byte],
opt1: Option[int], opt2: Option[string], opt3: Option[seq[byte]],
opt4: seq[int], opt5: seq[string],
opt6: seq[seq[byte]]) -> RestApiResponse:
if smp1.isErr():
return RestApiResponse.error(Http411, $smp1.error())
if smp2.isErr():
return RestApiResponse.error(Http412, $smp2.error())
if smp3.isErr():
return RestApiResponse.error(Http413, $smp3.error())
let o1 =
if opt1.isSome():
let res = opt1.get()
if res.isErr():
return RestApiResponse.error(Http414, $res.error())
$res.get()
else:
""
let o2 =
if opt2.isSome():
let res = opt2.get()
if res.isErr():
return RestApiResponse.error(Http415, $res.error())
res.get()
else:
""
let o3 =
if opt3.isSome():
let res = opt3.get()
if res.isErr():
return RestApiResponse.error(Http416, $res.error())
toHex(res.get())
else:
""
let o4 =
if opt4.isErr():
return RestApiResponse.error(Http417, $opt4.error())
else:
opt4.get().join(",")
let o5 =
if opt5.isErr():
return RestApiResponse.error(Http418, $opt5.error())
else:
opt5.get().join(",")
let o6 =
if opt6.isErr():
return RestApiResponse.error(Http421, $opt6.error())
else:
let binres = opt6.get()
var res = newSeq[string]()
for item in binres:
res.add(toHex(item))
res.join(",")
let body = $smp1.get() & ":" & smp2.get() & ":" & toHex(smp3.get()) &
":" & o1 & ":" & o2 & ":" & o3 &
":" & o4 & ":" & o5 & ":" & o6
return RestApiResponse.response(body)
const TestVectors = [
("/test/1/2/0xaa?opt1=1&opt2=2&opt3=0xbb&opt4=2&opt4=3&opt4=4&opt5=t&" &
"opt5=e&opt5=s&opt5=t&opt6=0xCA&opt6=0xFE",
ClientResponse.init(200, "1:2:aa:1:2:bb:2,3,4:t,e,s,t:ca,fe")),
# Optional argument will not pass decoding procedure `opt1=a`.
("/test/1/2/0xaa?opt1=a&opt2=2&opt3=0xbb&opt4=2&opt4=3&opt4=4&opt5=t&" &
"opt5=e&opt5=s&opt5=t&opt6=0xCA&opt6=0xFE",
ClientResponse.init(414)),
# Sequence argument will not pass decoding procedure `opt4=a`.
("/test/1/2/0xaa?opt1=1&opt2=2&opt3=0xbb&opt4=2&opt4=3&opt4=a&opt5=t&" &
"opt5=e&opt5=s&opt5=t&opt6=0xCA&opt6=0xFE",
ClientResponse.init(417)),
# Optional argument will not pass decoding procedure `opt3=0xxx`.
("/test/1/2/0xaa?opt1=1&opt2=2&opt3=0xxx&opt4=2&opt4=3&opt4=4&opt5=t&" &
"opt5=e&opt5=s&opt5=t&opt6=0xCA&opt6=0xFE",
ClientResponse.init(416)),
# Sequence argument will not pass decoding procedure `opt6=0xxx`.
("/test/1/2/0xaa?opt1=1&opt2=2&opt3=0xbb&opt4=2&opt4=3&opt4=5&opt5=t&" &
"opt5=e&opt5=s&opt5=t&opt6=0xCA&opt6=0xxx",
ClientResponse.init(421)),
# All optional arguments are missing
("/test/1/2/0xaa", ClientResponse.init(200, "1:2:aa::::::"))
]
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
for item in TestVectors:
let res = await httpClient(serverAddress, MethodGet,
item[0], "")
check res.status == item[1].status
if len(item[1].data) > 0:
check res.data == item[1].data
finally:
await server.closeWait()
asyncTest "Requests [path + query + request body] test":
var router = RestRouter.init(testValidate)
router.api(MethodPost, "/test/{smp1}/{smp2}/{smp3}") do (
smp1: int, smp2: string, smp3: seq[byte],
opt1: Option[int], opt2: Option[string], opt3: Option[seq[byte]],
opt4: seq[int], opt5: seq[string],
opt6: seq[seq[byte]],
contentBody: Option[ContentBody]) -> RestApiResponse:
if smp1.isErr():
return RestApiResponse.error(Http411, $smp1.error())
if smp2.isErr():
return RestApiResponse.error(Http412, $smp2.error())
if smp3.isErr():
return RestApiResponse.error(Http413, $smp3.error())
let o1 =
if opt1.isSome():
let res = opt1.get()
if res.isErr():
return RestApiResponse.error(Http414, $res.error())
$res.get()
else:
""
let o2 =
if opt2.isSome():
let res = opt2.get()
if res.isErr():
return RestApiResponse.error(Http415, $res.error())
res.get()
else:
""
let o3 =
if opt3.isSome():
let res = opt3.get()
if res.isErr():
return RestApiResponse.error(Http416, $res.error())
toHex(res.get())
else:
""
let o4 =
if opt4.isErr():
return RestApiResponse.error(Http417, $opt4.error())
else:
opt4.get().join(",")
let o5 =
if opt5.isErr():
return RestApiResponse.error(Http418, $opt5.error())
else:
opt5.get().join(",")
let o6 =
if opt6.isErr():
return RestApiResponse.error(Http421, $opt6.error())
else:
let binres = opt6.get()
var res = newSeq[string]()
for item in binres:
res.add(toHex(item))
res.join(",")
let obody =
if contentBody.isSome():
let body = contentBody.get()
$body.contentType & "," & bytesToString(body.data)
else:
"nobody"
let body = $smp1.get() & ":" & smp2.get() & ":" & toHex(smp3.get()) &
":" & o1 & ":" & o2 & ":" & o3 &
":" & o4 & ":" & o5 & ":" & o6 &
":" & obody
return RestApiResponse.response(body)
const PostVectors = [
(
("/test/1/2/0xaa", "text/plain", "textbody"),
ClientResponse.init(200, "1:2:aa:::::::text/plain,textbody")
),
(
("/test/1/2/0xaa", "", ""),
ClientResponse.init(200)
),
(
("/test/1/2/0xaa", "text/plain", ""),
ClientResponse.init(200, "1:2:aa:::::::nobody")
),
(
("/test/1/2/0xaa?opt1=1&opt2=2&opt3=0xbb&opt4=2&opt4=3&opt4=4&opt5=t&" &
"opt5=e&opt5=s&opt5=t&opt6=0xCA&opt6=0xFE", "text/plain", "textbody"),
ClientResponse.init(200,
"1:2:aa:1:2:bb:2,3,4:t,e,s,t:ca,fe:text/plain,textbody")
)
]
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
for item in PostVectors:
let req = item[0]
let res = await httpClient(serverAddress, MethodPost,
req[0], req[2], req[1])
check res.status == item[1].status
if len(item[1].data) > 0:
check res.data == item[1].data
finally:
await server.closeWait()
asyncTest "Direct response manipulation test":
var router = RestRouter.init(testValidate)
router.api(MethodGet, "/test/{smp1}") do (
smp1: int, opt1: Option[int], opt4: seq[int],
resp: HttpResponseRef) -> RestApiResponse:
if smp1.isErr():
return RestApiResponse.error(Http411, $smp1.error())
let o1 =
if opt1.isSome():
let res = opt1.get()
if res.isErr():
return RestApiResponse.error(Http414, $res.error())
$res.get()
else:
""
let o4 =
if opt4.isErr():
return RestApiResponse.error(Http417, $opt4.error())
else:
opt4.get().join(",")
let path = smp1.get()
let restResp = $smp1.get() & ":" & o1 & ":" & o4
case path
of 1:
await resp.sendBody(restResp)
of 2:
await resp.sendBody(restResp)
return RestApiResponse.response("")
of 3:
await resp.sendBody(restResp)
return RestApiResponse.error(Http422, "error")
else:
return RestApiResponse.error(Http426, "error")
router.api(MethodPost, "/test/{smp1}") do (
smp1: int, opt1: Option[int], opt4: seq[int],
body: Option[ContentBody],
resp: HttpResponseRef) -> RestApiResponse:
if smp1.isErr():
return RestApiResponse.error(Http411, $smp1.error())
let o1 =
if opt1.isSome():
let res = opt1.get()
if res.isErr():
return RestApiResponse.error(Http414, $res.error())
$res.get()
else:
""
let o4 =
if opt4.isErr():
return RestApiResponse.error(Http417, $opt4.error())
else:
opt4.get().join(",")
let obody =
if body.isSome():
let b = body.get()
$b.contentType & "," & bytesToString(b.data)
else:
"nobody"
let path = smp1.get()
let restResp = $smp1.get() & ":" & o1 & ":" & o4 & ":" & obody
case path
of 1:
await resp.sendBody(restResp)
of 2:
await resp.sendBody(restResp)
return RestApiResponse.response("some result")
of 3:
await resp.sendBody(restResp)
return RestApiResponse.error(Http422, "error")
else:
return RestApiResponse.error(Http426, "error")
const PostVectors = [
(
# Empty result with response sent via `resp`.
("/test/1?opt1=2345&opt4=3456&opt4=4567&opt4=5678&opt4=6789",
"text/plain", "somebody"),
ClientResponse.init(200,
"1:2345:3456,4567,5678,6789:text/plain,somebody")
),
(
# Result with response sent via `resp`.
("/test/2?opt1=2345&opt4=3456&opt4=4567&opt4=5678&opt4=6789",
"text/plain", "somebody"),
ClientResponse.init(200,
"2:2345:3456,4567,5678,6789:text/plain,somebody")
),
(
# Error with response sent via `resp`.
("/test/3?opt1=2345&opt4=3456&opt4=4567&opt4=5678&opt4=6789",
"text/plain", "somebody"),
ClientResponse.init(200,
"3:2345:3456,4567,5678,6789:text/plain,somebody")
)
]
const GetVectors = [
(
# Empty result with response sent via `resp`.
"/test/1?opt1=2345&opt4=3456&opt4=4567&opt4=5678&opt4=6789",
ClientResponse.init(200, "1:2345:3456,4567,5678,6789")
),
(
# Result with response sent via `resp`.
"/test/2?opt1=2345&opt4=3456&opt4=4567&opt4=5678&opt4=6789",
ClientResponse.init(200, "2:2345:3456,4567,5678,6789")
),
(
# Error with response sent via `resp`.
"/test/3?opt1=2345&opt4=3456&opt4=4567&opt4=5678&opt4=6789",
ClientResponse.init(200, "3:2345:3456,4567,5678,6789")
)
]
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
for item in GetVectors:
let res = await httpClient(serverAddress, MethodGet, item[0], "")
check res.status == item[1].status
if len(item[1].data) > 0:
check res.data == item[1].data
for item in PostVectors:
let req = item[0]
let res = await httpClient(serverAddress, MethodPost,
req[0], req[2], req[1])
check res.status == item[1].status
if len(item[1].data) > 0:
check res.data == item[1].data
finally:
await server.closeWait()
asyncTest "Responses with headers test":
var router = RestRouter.init(testValidate)
router.api(MethodGet, "/test/get/success") do (
param: Option[string]) -> RestApiResponse:
let test = param.get().get()
case test
of "test1":
let headers = [
("test-header", "SUCCESS"), ("test-header", "TEST"),
("test-header", "1")
]
return RestApiResponse.response("TEST1:OK", Http200, headers = headers)
of "test2":
let headers = HttpTable.init([
("test-header", "SUCCESS"), ("test-header", "TEST"),
("test-header", "2")
])
return RestApiResponse.response("TEST2:OK", Http200, headers = headers)
of "test3":
let headers = HttpTable.init([
("test-header", "SUCCESS"), ("test-header", "TEST"),
("test-header", "3"), ("content-type", "application/success")
])
return RestApiResponse.response("TEST3:OK", Http200,
contentType = "text/success",
headers = headers)
else:
return RestApiResponse.error(Http400)
router.api(MethodGet, "/test/get/error") do (
param: Option[string]) -> RestApiResponse:
let testName = param.get().get()
case testName
of "test1":
let headers = [
("test-header", "ERROR"), ("test-header", "TEST"),
("test-header", "1")
]
return RestApiResponse.error(Http404, "ERROR1:OK", headers = headers)
of "test2":
let headers = HttpTable.init([
("test-header", "ERROR"), ("test-header", "TEST"),
("test-header", "2")
])
return RestApiResponse.error(Http404, "ERROR2:OK", headers = headers)
of "test3":
let headers = HttpTable.init([
("test-header", "ERROR"), ("test-header", "TEST"),
("test-header", "3"), ("content-type", "application/error")
])
return RestApiResponse.error(Http404, "ERROR3:OK",
contentType = "text/error",
headers = headers)
else:
return RestApiResponse.error(Http400)
router.api(MethodGet, "/test/get/redirect") do (
param: Option[string]) -> RestApiResponse:
let testName = param.get().get()
case testName
of "test1":
let headers = [
("test-header", "REDIRECT"), ("test-header", "TEST"),
("test-header", "1")
]
return RestApiResponse.redirect(Http307, "/test/get/redirect1",
preserveQuery = true, headers = headers)
of "test2":
let headers = HttpTable.init([
("test-header", "REDIRECT"), ("test-header", "TEST"),
("test-header", "2")
])
return RestApiResponse.redirect(Http307, "/test/get/redirect2",
preserveQuery = false,
headers = headers)
of "test3":
let headers = HttpTable.init([
("test-header", "REDIRECT"), ("test-header", "TEST"),
("test-header", "3"), ("location", "/test/get/wrong_redirect")
])
return RestApiResponse.redirect(Http307, "/test/get/redirect3",
preserveQuery = true,
headers = headers)
else:
return RestApiResponse.error(Http400)
const HttpHeadersVectors = [
("/test/get/success?param=test1",
ClientResponse.init(200, "TEST1:OK",
[("test-header", "SUCCESS"), ("test-header", "TEST"),
("test-header", "1")])),
("/test/get/success?param=test2",
ClientResponse.init(200, "TEST2:OK",
[("test-header", "SUCCESS"), ("test-header", "TEST"),
("test-header", "2")])),
("/test/get/success?param=test3",
ClientResponse.init(200, "TEST3:OK",
[("test-header", "SUCCESS"), ("test-header", "TEST"),
("test-header", "3"), ("content-type", "text/success")])),
("/test/get/error?param=test1",
ClientResponse.init(404, "ERROR1:OK",
[("test-header", "ERROR"), ("test-header", "TEST"),
("test-header", "1")])),
("/test/get/error?param=test2",
ClientResponse.init(404, "ERROR2:OK",
[("test-header", "ERROR"), ("test-header", "TEST"),
("test-header", "2")])),
("/test/get/error?param=test3",
ClientResponse.init(404, "ERROR3:OK",
[("test-header", "ERROR"), ("test-header", "TEST"),
("test-header", "3"), ("content-type", "text/error")])),
("/test/get/redirect?param=test1",
ClientResponse.init(307, "",
[("test-header", "REDIRECT"), ("test-header", "TEST"),
("test-header", "1"),
("location", "/test/get/redirect1?param=test1")])),
("/test/get/redirect?param=test2",
ClientResponse.init(307, "",
[("test-header", "REDIRECT"), ("test-header", "TEST"),
("test-header", "2"),
("location", "/test/get/redirect2")])),
("/test/get/redirect?param=test3",
ClientResponse.init(307, "",
[("test-header", "REDIRECT"), ("test-header", "TEST"),
("test-header", "3"),
("location", "/test/get/redirect3?param=test3")])),
]
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
for item in HttpHeadersVectors:
let res = await httpClient(serverAddress, MethodGet, item[0], "")
check cmpWithHeaders(res, item[1])
finally:
await server.closeWait()
asyncTest "Responses without content test":
var router = RestRouter.init(testValidate)
router.api(MethodGet, "/test/get/nocontent") do (
param: Option[string]) -> RestApiResponse:
let testName = param.get().get()
case testName
of "test1":
return RestApiResponse.response()
of "test2":
let headers = HttpTable.init([("test-header", "NORESPONSE2"),
("test-header", "2")])
return RestApiResponse.response(Http202, headers)
of "test3":
let headers = [("test-header", "NORESPONSE3"),
("test-header", "3")]
return RestApiResponse.response(Http203, headers)
else:
return RestApiResponse.error(Http400)
router.api(MethodPost, "/test/post/nocontent") do (
param: Option[string]) -> RestApiResponse:
let testName = param.get().get()
case testName
of "test1":
return RestApiResponse.response()
of "test2":
let headers = HttpTable.init([("test-header", "NORESPONSE2"),
("test-header", "2")])
return RestApiResponse.response(Http202, headers)
of "test3":
let headers = [("test-header", "NORESPONSE3"),
("test-header", "3")]
return RestApiResponse.response(Http203, headers)
else:
return RestApiResponse.error(Http400)
const HttpGetHeadersVectors = [
("/test/get/nocontent?param=test1",
ClientResponse.init(200, [])),
("/test/get/nocontent?param=test2",
ClientResponse.init(202,
[("test-header", "NORESPONSE2"), ("test-header", "2")])),
("/test/get/nocontent?param=test3",
ClientResponse.init(203,
[("test-header", "NORESPONSE3"), ("test-header", "3")]))
]
const HttpPostHeadersVectors = [
("/test/post/nocontent?param=test1",
ClientResponse.init(200, [])),
("/test/post/nocontent?param=test2",
ClientResponse.init(202,
[("test-header", "NORESPONSE2"), ("test-header", "2")])),
("/test/post/nocontent?param=test3",
ClientResponse.init(203,
[("test-header", "NORESPONSE3"), ("test-header", "3")]))
]
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
for item in HttpGetHeadersVectors:
let res = await httpClient(serverAddress, MethodGet, item[0], "")
check:
cmpNoContent(res, item[1])
len(res.data) == 0
res.headers.getString("content-type") == ""
for item in HttpPostHeadersVectors:
let res = await httpClient(serverAddress, MethodPost, item[0], "")
check:
cmpNoContent(res, item[1])
len(res.data) == 0
res.headers.getString("content-type") == ""
finally:
await server.closeWait()
asyncTest "preferredContentType() test":
const PostVectors = [
(
("/test/post", "somebody0908", "text/html",
"app/type1;q=0.9,app/type2;q=0.8"),
ClientResponse.init(200, "type1[text/html,somebody0908]")
),
(
("/test/post", "somebody0908", "text/html",
"app/type2;q=0.8,app/type1;q=0.9"),
ClientResponse.init(200, "type1[text/html,somebody0908]")
),
(
("/test/post", "somebody09", "text/html",
"app/type2,app/type1;q=0.9"),
ClientResponse.init(200, "type2[text/html,somebody09]")
),
(
("/test/post", "somebody09", "text/html", "app/type1;q=0.9,app/type2"),
ClientResponse.init(200, "type2[text/html,somebody09]")
),
(
("/test/post", "somebody", "text/html", "*/*"),
ClientResponse.init(200, "type1[text/html,somebody]")
),
(
("/test/post", "somebody", "text/html", ""),
ClientResponse.init(200, "type1[text/html,somebody]")
),
(
("/test/post", "somebody", "text/html", "app/type2"),
ClientResponse.init(200, "type2[text/html,somebody]")
),
(
("/test/post", "somebody", "text/html", "app/type3"),
ClientResponse.init(406, "")
)
]
var router = RestRouter.init(testValidate)
router.api(MethodPost, "/test/post") do (
body: Option[ContentBody],
resp: HttpResponseRef) -> RestApiResponse:
let obody =
if body.isSome():
let b = body.get()
$b.contentType & "," & bytesToString(b.data)
else:
"nobody"
let preferred = preferredContentType(testMediaType1, testMediaType2)
return
if preferred.isOk():
if preferred.get() == testMediaType1:
RestApiResponse.response("type1[" & obody & "]")
elif preferred.get() == testMediaType2:
RestApiResponse.response("type2[" & obody & "]")
else:
# This MUST not be happened.
RestApiResponse.error(Http407, "")
else:
RestApiResponse.error(Http406, "")
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
for item in PostVectors:
let res = await httpClient(serverAddress, MethodPost, item[0][0],
item[0][1], item[0][2], item[0][3])
check:
res.status == item[1].status
res.data == item[1].data
finally:
await server.closeWait()
asyncTest "Handle raw requests inside api handler test":
var router = RestRouter.init(testValidate)
router.rawApi(MethodPost, "/test/post") do () -> RestApiResponse:
let contentType = request.headers.getString(ContentTypeHeader)
let body = await request.getBody()
return
RestApiResponse.response(
"type[" & contentType & ":" & body.toHex() & "]")
var sres = RestServerRef.new(router, serverAddress)
let server = sres.get()
server.start()
try:
let res = await httpClient(serverAddress, MethodPost, "/test/post",
"0123456789", "application/octet-stream")
check:
res.status == 200
res.data == "type[application/octet-stream:30313233343536373839]"
finally:
await server.closeWait()
asyncTest "API endpoints with metrics enabled test":
var router = RestRouter.init(testValidate)
router.metricsApi(MethodGet, "/test/get/1", {}) do () -> RestApiResponse:
return RestApiResponse.response("ok-1", Http200, "test/test")
router.metricsApi(MethodGet, "/test/get/2",
{RestServerMetricsType.Status}) do () -> RestApiResponse:
return RestApiResponse.response("ok-2", Http200, "test/test")
router.metricsApi(MethodGet, "/test/get/3",
{Response}) do () -> RestApiResponse:
return RestApiResponse.response("ok-3", Http200, "test/test")
router.metricsApi(MethodGet, "/test/get/4",
{RestServerMetricsType.Status,
Response}) do () -> RestApiResponse:
return RestApiResponse.response("ok-4", Http200, "test/test")
router.metricsApi(MethodGet, "/test/get/5",
RestServerMetrics) do () -> RestApiResponse:
return RestApiResponse.response("ok-5", Http200, "test/test")
router.rawMetricsApi(MethodGet, "/test/get/6", {}) do () -> RestApiResponse:
return RestApiResponse.response("ok-6", Http200, "test/test")
router.rawMetricsApi(MethodGet, "/test/get/7",
{RestServerMetricsType.Status}) do () -> RestApiResponse:
return RestApiResponse.response("ok-7", Http200, "test/test")
router.rawMetricsApi(MethodGet, "/test/get/8",
{Response}) do () -> RestApiResponse:
return RestApiResponse.response("ok-8", Http200, "test/test")
router.rawMetricsApi(MethodGet, "/test/get/9",
{RestServerMetricsType.Status,
Response}) do () -> RestApiResponse:
return RestApiResponse.response("ok-9", Http200, "test/test")
router.rawMetricsApi(MethodGet, "/test/get/10",
RestServerMetrics) do () -> RestApiResponse:
return RestApiResponse.response("ok-10", Http200, "test/test")
test "Leaks test":
checkLeaks()