nim-json-rpc/tests/testrpcmacro.nim

375 lines
11 KiB
Nim

# json-rpc
# Copyright (c) 2019-2023 Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import
unittest2,
chronicles,
../json_rpc/rpcserver,
./private/helpers,
json_serialization/std/options
type
# some nested types to check object parsing
Test2 = object
x: array[0..2, int]
y: string
Test = object
a: array[0..1, int]
b: Test2
MyObject = object
a: int
b: Test
c: float
MyOptional = object
maybeInt: Option[int]
MyOptionalNotBuiltin = object
val: Option[Test2]
MyEnum = enum
Enum0
Enum1
MuscleCar = object
color: string
wheel: int
MyObject.useDefaultSerializationIn JrpcConv
Test.useDefaultSerializationIn JrpcConv
Test2.useDefaultSerializationIn JrpcConv
MyOptional.useDefaultSerializationIn JrpcConv
MyOptionalNotBuiltin.useDefaultSerializationIn JrpcConv
MuscleCar.useDefaultSerializationIn JrpcConv
proc readValue*(r: var JsonReader[JrpcConv], val: var MyEnum)
{.gcsafe, raises: [IOError, SerializationError].} =
let intVal = r.parseInt(int)
if intVal < low(MyEnum).int or intVal > high(MyEnum).int:
r.raiseUnexpectedValue("invalid enum range " & $intVal)
val = MyEnum(intVal)
let
testObj = %*{
"a": %1,
"b": %*{
"a": %[5, 0],
"b": %*{
"x": %[1, 2, 3],
"y": %"test"
}
},
"c": %1.0}
var s = newRpcSocketServer(["127.0.0.1:0"])
# RPC definitions
s.rpc("rpc.simplePath"):
return %1
s.rpc("rpc.enumParam") do(e: MyEnum):
return %[$e]
s.rpc("rpc.differentParams") do(a: int, b: string):
return %[%a, %b]
s.rpc("rpc.arrayParam") do(arr: array[0..5, byte], b: string):
var res = %arr
res.add %b
return %res
s.rpc("rpc.seqParam") do(a: string, s: seq[int]):
var res = newJArray()
res.add %a
for item in s:
res.add %int(item)
return res
s.rpc("rpc.objParam") do(a: string, obj: MyObject):
return %obj
s.rpc("rpc.returnTypeSimple") do(i: int) -> int:
return i
s.rpc("rpc.returnTypeComplex") do(i: int) -> Test2:
return Test2(x: [1, i, 3], y: "test")
s.rpc("rpc.testReturns") do() -> int:
return 1234
s.rpc("rpc.multiVarsOfOneType") do(a, b: string) -> string:
return a & " " & b
s.rpc("rpc.optional") do(obj: MyOptional) -> MyOptional:
return obj
s.rpc("rpc.optionalArg") do(val: int, obj: Option[MyOptional]) -> MyOptional:
return if obj.isSome():
obj.get()
else:
MyOptional(maybeInt: some(val))
s.rpc("rpc.optionalArg2") do(a, b: string, c, d: Option[string]) -> string:
var ret = a & b
if c.isSome: ret.add c.get()
if d.isSome: ret.add d.get()
return ret
s.rpc("echo") do(car: MuscleCar) -> JsonString:
return JrpcConv.encode(car).JsonString
type
OptionalFields = object
a: int
b: Option[int]
c: string
d: Option[int]
e: Option[string]
OptionalFields.useDefaultSerializationIn JrpcConv
s.rpc("rpc.mixedOptionalArg") do(a: int, b: Option[int], c: string,
d: Option[int], e: Option[string]) -> OptionalFields:
result.a = a
result.b = b
result.c = c
result.d = d
result.e = e
s.rpc("rpc.optionalArgNotBuiltin") do(obj: Option[MyOptionalNotBuiltin]) -> string:
return if obj.isSome:
let val = obj.get.val
if val.isSome:
obj.get.val.get.y
else:
"Empty2"
else:
"Empty1"
type
MaybeOptions = object
o1: Option[bool]
o2: Option[bool]
o3: Option[bool]
MaybeOptions.useDefaultSerializationIn JrpcConv
s.rpc("rpc.optInObj") do(data: string, options: Option[MaybeOptions]) -> int:
if options.isSome:
let o = options.get
if o.o1.isSome: result += 1
if o.o2.isSome: result += 2
if o.o3.isSome: result += 4
proc installMoreApiHandlers*(s: RpcServer, prefix: static string) =
s.rpc(prefix & ".optionalStringArg") do(a: Option[string]) -> string:
if a.isSome:
return a.get()
else:
return "nope"
s.installMoreApiHandlers("rpc")
# Tests
suite "Server types":
test "On macro registration":
check s.hasMethod("rpc.simplePath")
check s.hasMethod("rpc.differentParams")
check s.hasMethod("rpc.arrayParam")
check s.hasMethod("rpc.seqParam")
check s.hasMethod("rpc.objParam")
check s.hasMethod("rpc.returnTypeSimple")
check s.hasMethod("rpc.returnTypeComplex")
check s.hasMethod("rpc.testReturns")
check s.hasMethod("rpc.multiVarsOfOneType")
check s.hasMethod("rpc.optionalArg")
check s.hasMethod("rpc.mixedOptionalArg")
check s.hasMethod("rpc.optionalArgNotBuiltin")
check s.hasMethod("rpc.optInObj")
check s.hasMethod("rpc.optionalStringArg")
test "Simple paths":
let r = waitFor s.executeMethod("rpc.simplePath", %[])
check r == "1"
test "Enum param paths":
block:
let r = waitFor s.executeMethod("rpc.enumParam", %[%int64(Enum1)])
check r == "[\"Enum1\"]"
expect(JsonRpcError):
discard waitFor s.executeMethod("rpc.enumParam", %[(int64(42))])
test "Different param types":
let
inp = %[%1, %"abc"]
r = waitFor s.executeMethod("rpc.differentParams", inp)
check r == inp
test "Array parameters":
let r1 = waitFor s.executeMethod("rpc.arrayParam", %[%[1, 2, 3], %"hello"])
var ckR1 = %[1, 2, 3, 0, 0, 0]
ckR1.elems.add %"hello"
check r1 == ckR1
test "Seq parameters":
let r2 = waitFor s.executeMethod("rpc.seqParam", %[%"abc", %[1, 2, 3, 4, 5]])
var ckR2 = %["abc"]
for i in 0..4: ckR2.add %(i + 1)
check r2 == ckR2
test "Object parameters":
let r = waitFor s.executeMethod("rpc.objParam", %[%"abc", testObj])
check r == testObj
test "Simple return types":
let
inp = %99
r1 = waitFor s.executeMethod("rpc.returnTypeSimple", %[%inp])
check r1 == inp
test "Complex return types":
let
inp = 99
r1 = waitFor s.executeMethod("rpc.returnTypeComplex", %[%inp])
check r1 == %*{"x": %[1, inp, 3], "y": "test"}
test "Option types":
let
inp1 = MyOptional(maybeInt: some(75))
inp2 = MyOptional()
r1 = waitFor s.executeMethod("rpc.optional", %[%inp1])
r2 = waitFor s.executeMethod("rpc.optional", %[%inp2])
check r1.string == JrpcConv.encode inp1
check r2.string == JrpcConv.encode inp2
test "Return statement":
let r = waitFor s.executeMethod("rpc.testReturns", %[])
check r == JrpcConv.encode 1234
test "Runtime errors":
expect JsonRpcError:
# root param not array
discard waitFor s.executeMethod("rpc.arrayParam", %"test")
expect JsonRpcError:
# too big for array
discard waitFor s.executeMethod("rpc.arrayParam", %[%[0, 1, 2, 3, 4, 5, 6], %"hello"])
expect JsonRpcError:
# wrong sub parameter type
discard waitFor s.executeMethod("rpc.arrayParam", %[%"test", %"hello"])
expect JsonRpcError:
# wrong param type
discard waitFor s.executeMethod("rpc.differentParams", %[%"abc", %1])
test "Multiple variables of one type":
let r = waitFor s.executeMethod("rpc.multiVarsOfOneType", %[%"hello", %"world"])
check r == JrpcConv.encode "hello world"
test "Optional arg":
let
int1 = MyOptional(maybeInt: some(75))
int2 = MyOptional(maybeInt: some(117))
r1 = waitFor s.executeMethod("rpc.optionalArg", %[%117, %int1])
r2 = waitFor s.executeMethod("rpc.optionalArg", %[%117])
r3 = waitFor s.executeMethod("rpc.optionalArg", %[%117, newJNull()])
check r1 == JrpcConv.encode int1
check r2 == JrpcConv.encode int2
check r3 == JrpcConv.encode int2
test "Optional arg2":
let r1 = waitFor s.executeMethod("rpc.optionalArg2", %[%"A", %"B"])
check r1 == JrpcConv.encode "AB"
let r2 = waitFor s.executeMethod("rpc.optionalArg2", %[%"A", %"B", newJNull()])
check r2 == JrpcConv.encode "AB"
let r3 = waitFor s.executeMethod("rpc.optionalArg2", %[%"A", %"B", newJNull(), newJNull()])
check r3 == JrpcConv.encode "AB"
let r4 = waitFor s.executeMethod("rpc.optionalArg2", %[%"A", %"B", newJNull(), %"D"])
check r4 == JrpcConv.encode "ABD"
let r5 = waitFor s.executeMethod("rpc.optionalArg2", %[%"A", %"B", %"C", %"D"])
check r5 == JrpcConv.encode "ABCD"
let r6 = waitFor s.executeMethod("rpc.optionalArg2", %[%"A", %"B", %"C", newJNull()])
check r6 == JrpcConv.encode "ABC"
let r7 = waitFor s.executeMethod("rpc.optionalArg2", %[%"A", %"B", %"C"])
check r7 == JrpcConv.encode "ABC"
test "Mixed optional arg":
var ax = waitFor s.executeMethod("rpc.mixedOptionalArg", %[%10, %11, %"hello", %12, %"world"])
check ax == JrpcConv.encode OptionalFields(a: 10, b: some(11), c: "hello", d: some(12), e: some("world"))
var bx = waitFor s.executeMethod("rpc.mixedOptionalArg", %[%10, newJNull(), %"hello"])
check bx == JrpcConv.encode OptionalFields(a: 10, c: "hello")
test "Non-built-in optional types":
let
t2 = Test2(x: [1, 2, 3], y: "Hello")
testOpts1 = MyOptionalNotBuiltin(val: some(t2))
testOpts2 = MyOptionalNotBuiltin()
var r = waitFor s.executeMethod("rpc.optionalArgNotBuiltin", %[%testOpts1])
check r == JrpcConv.encode t2.y
var r2 = waitFor s.executeMethod("rpc.optionalArgNotBuiltin", %[])
check r2 == JrpcConv.encode "Empty1"
var r3 = waitFor s.executeMethod("rpc.optionalArgNotBuiltin", %[%testOpts2])
check r3 == JrpcConv.encode "Empty2"
test "Manually set up JSON for optionals":
# Check manual set up json with optionals
let opts1 = parseJson("""{"o1": true}""")
var r1 = waitFor s.executeMethod("rpc.optInObj", %[%"0x31ded", opts1])
check r1 == JrpcConv.encode 1
let opts2 = parseJson("""{"o2": true}""")
var r2 = waitFor s.executeMethod("rpc.optInObj", %[%"0x31ded", opts2])
check r2 == JrpcConv.encode 2
let opts3 = parseJson("""{"o3": true}""")
var r3 = waitFor s.executeMethod("rpc.optInObj", %[%"0x31ded", opts3])
check r3 == JrpcConv.encode 4
# Combinations
let opts4 = parseJson("""{"o1": true, "o3": true}""")
var r4 = waitFor s.executeMethod("rpc.optInObj", %[%"0x31ded", opts4])
check r4 == JrpcConv.encode 5
let opts5 = parseJson("""{"o2": true, "o3": true}""")
var r5 = waitFor s.executeMethod("rpc.optInObj", %[%"0x31ded", opts5])
check r5 == JrpcConv.encode 6
let opts6 = parseJson("""{"o1": true, "o2": true}""")
var r6 = waitFor s.executeMethod("rpc.optInObj", %[%"0x31ded", opts6])
check r6 == JrpcConv.encode 3
test "Optional String Arg":
let
data = some("some string")
r1 = waitFor s.executeMethod("rpc.optionalStringArg", %[%data])
r2 = waitFor s.executeMethod("rpc.optionalStringArg", %[])
r3 = waitFor s.executeMethod("rpc.optionalStringArg", %[newJNull()])
check r1 == %data.get()
check r2 == %"nope"
check r3 == %"nope"
test "Null object fields":
let r = waitFor s.executeMethod("echo", """{"car":{"color":"red","wheel":null}}""".JsonString)
check r == """{"color":"red","wheel":0}"""
let x = waitFor s.executeMethod("echo", """{"car":{"color":null,"wheel":77}}""".JsonString)
check x == """{"color":"","wheel":77}"""
let y = waitFor s.executeMethod("echo", """{"car":null}""".JsonString)
check y == """{"color":"","wheel":0}"""
let z = waitFor s.executeMethod("echo", "[null]".JsonString)
check z == """{"color":"","wheel":0}"""
s.stop()
waitFor s.closeWait()