264 lines
6.4 KiB
Nim
264 lines
6.4 KiB
Nim
import
|
|
strutils, unittest, json,
|
|
serialization/object_serialization,
|
|
serialization/testing/generic_suite,
|
|
../json_serialization, ./utils,
|
|
../json_serialization/std/[options, sets, tables]
|
|
|
|
type
|
|
Foo = object
|
|
i: int
|
|
b {.dontSerialize.}: Bar
|
|
s: string
|
|
|
|
Bar = object
|
|
sf: seq[Foo]
|
|
z: ref Simple
|
|
|
|
Invalid = object
|
|
distance: Mile
|
|
|
|
HasUnusualFieldNames = object
|
|
# Using Nim reserved keyword
|
|
`type`: string
|
|
renamedField {.serializedFieldName("renamed").}: string
|
|
|
|
MyKind = enum
|
|
Apple
|
|
Banana
|
|
|
|
MyCaseObject = object
|
|
name: string
|
|
case kind: MyKind
|
|
of Banana: banana: int
|
|
of Apple: apple: string
|
|
|
|
MyUseCaseObject = object
|
|
field: MyCaseObject
|
|
|
|
HasJsonString = object
|
|
name: string
|
|
data: JsonString
|
|
id: int
|
|
|
|
HasJsonNode = object
|
|
name: string
|
|
data: JsonNode
|
|
id: int
|
|
|
|
# TODO `borrowSerialization` still doesn't work
|
|
# properly when it's placed in another module:
|
|
Meter.borrowSerialization int
|
|
|
|
template reject(code) {.used.} =
|
|
static: doAssert(not compiles(code))
|
|
|
|
proc `==`(lhs, rhs: Meter): bool =
|
|
int(lhs) == int(rhs)
|
|
|
|
proc `==`(lhs, rhs: ref Simple): bool =
|
|
if lhs.isNil: return rhs.isNil
|
|
if rhs.isNil: return false
|
|
return lhs[] == rhs[]
|
|
|
|
executeReaderWriterTests Json
|
|
|
|
proc newSimple(x: int, y: string, d: Meter): ref Simple =
|
|
new result
|
|
result.x = x
|
|
result.y = y
|
|
result.distance = d
|
|
|
|
var invalid = Invalid(distance: Mile(100))
|
|
# The compiler cannot handle this check at the moment
|
|
# {.fatal.} seems fatal even in `compiles` context
|
|
when false: reject invalid.toJson
|
|
else: discard invalid
|
|
|
|
suite "toJson tests":
|
|
test "encode primitives":
|
|
check:
|
|
1.toJson == "1"
|
|
"".toJson == "\"\""
|
|
"abc".toJson == "\"abc\""
|
|
|
|
test "simple objects":
|
|
var s = Simple(x: 10, y: "test", distance: Meter(20))
|
|
|
|
check:
|
|
s.toJson == """{"distance":20,"x":10,"y":"test"}"""
|
|
s.toJson(typeAnnotations = true) == """{"$type":"Simple","distance":20,"x":10,"y":"test"}"""
|
|
s.toJson(pretty = true) == dedent"""
|
|
{
|
|
"distance": 20,
|
|
"x": 10,
|
|
"y": "test"
|
|
}
|
|
"""
|
|
|
|
test "handle missing fields":
|
|
let json = dedent"""
|
|
{
|
|
"distance": 20,
|
|
"y": "test"
|
|
}
|
|
"""
|
|
|
|
let decoded = Json.decode(json, Simple)
|
|
|
|
check:
|
|
decoded.x == 0
|
|
decoded.y == "test"
|
|
decoded.distance.int == 20
|
|
|
|
test "handle additional fields":
|
|
let json = dedent"""
|
|
{
|
|
"x": -20,
|
|
"futureObject": {"a": -1, "b": [1, 2.0, 3.1], "c": null, "d": true},
|
|
"futureBool": false,
|
|
"y": "y value"
|
|
}
|
|
"""
|
|
|
|
let decoded = Json.decode(json, Simple, allowUnknownFields = true)
|
|
|
|
check:
|
|
decoded.x == -20
|
|
decoded.y == "y value"
|
|
decoded.distance.int == 0
|
|
|
|
expect UnexpectedField:
|
|
let shouldNotDecode = Json.decode(json, Simple)
|
|
echo "This should not have decoded ", shouldNotDecode
|
|
|
|
test "arrays are printed correctly":
|
|
var x = HoldsArray(data: @[1, 2, 3, 4])
|
|
|
|
check:
|
|
x.toJson(pretty = true) == dedent"""
|
|
{
|
|
"data": [
|
|
1,
|
|
2,
|
|
3,
|
|
4
|
|
]
|
|
}
|
|
"""
|
|
|
|
test "max unsigned value":
|
|
var uintVal = not uint64(0)
|
|
let jsonValue = Json.encode(uintVal)
|
|
check:
|
|
jsonValue == "18446744073709551615"
|
|
Json.decode(jsonValue, uint64) == uintVal
|
|
|
|
expect JsonReaderError:
|
|
discard Json.decode(jsonValue, uint64, mode = Portable)
|
|
|
|
test "Unusual field names":
|
|
let r = HasUnusualFieldNames(`type`: "uint8", renamedField: "field")
|
|
check:
|
|
r.toJSON == """{"type":"uint8","renamed":"field"}"""
|
|
r == Json.decode("""{"type":"uint8", "renamed":"field"}""", HasUnusualFieldNames)
|
|
|
|
test "Option types":
|
|
let
|
|
h1 = HoldsOption(o: some Simple(x: 1, y: "2", distance: Meter(3)))
|
|
h2 = HoldsOption(r: newSimple(1, "2", Meter(3)))
|
|
|
|
Json.roundtripTest h1, """{"r":null,"o":{"distance":3,"x":1,"y":"2"}}"""
|
|
Json.roundtripTest h2, """{"r":{"distance":3,"x":1,"y":"2"},"o":null}"""
|
|
|
|
test "Case object as field":
|
|
let
|
|
original = MyUseCaseObject(field: MyCaseObject(name: "hello",
|
|
kind: Apple,
|
|
apple: "world"))
|
|
decoded = Json.decode(Json.encode(original), MyUseCaseObject)
|
|
|
|
check:
|
|
$original == $decoded
|
|
|
|
test "stringLike":
|
|
check:
|
|
"abc" == Json.decode(Json.encode(['a', 'b', 'c']), string)
|
|
"abc" == Json.decode(Json.encode(@['a', 'b', 'c']), string)
|
|
['a', 'b', 'c'] == Json.decode(Json.encode(@['a', 'b', 'c']), seq[char])
|
|
['a', 'b', 'c'] == Json.decode(Json.encode("abc"), seq[char])
|
|
['a', 'b', 'c'] == Json.decode(Json.encode(@['a', 'b', 'c']), array[3, char])
|
|
|
|
expect JsonReaderError: # too short
|
|
discard Json.decode(Json.encode(@['a', 'b']), array[3, char])
|
|
|
|
expect JsonReaderError: # too long
|
|
discard Json.decode(Json.encode(@['a', 'b']), array[1, char])
|
|
|
|
proc testJsonHolders(HasJsonData: type) =
|
|
let
|
|
data1 = dedent"""
|
|
{
|
|
"name": "Data 1",
|
|
"data": [1, 2, 3, 4],
|
|
"id": 101
|
|
}
|
|
"""
|
|
let
|
|
data2 = dedent"""
|
|
{
|
|
"name": "Data 2",
|
|
"data": "some string",
|
|
"id": 1002
|
|
}
|
|
"""
|
|
let
|
|
data3 = dedent"""
|
|
{
|
|
"name": "Data 3",
|
|
"data": {"field1": 10, "field2": [1, 2, 3], "field3": "test"},
|
|
"id": 10003
|
|
}
|
|
"""
|
|
|
|
try:
|
|
let
|
|
d1 = Json.decode(data1, HasJsonData)
|
|
d2 = Json.decode(data2, HasJsonData)
|
|
d3 = Json.decode(data3, HasJsonData)
|
|
|
|
check:
|
|
d1.name == "Data 1"
|
|
$d1.data == "[1,2,3,4]"
|
|
d1.id == 101
|
|
|
|
d2.name == "Data 2"
|
|
$d2.data == "\"some string\""
|
|
d2.id == 1002
|
|
|
|
d3.name == "Data 3"
|
|
$d3.data == """{"field1":10,"field2":[1,2,3],"field3":"test"}"""
|
|
d3.id == 10003
|
|
|
|
let
|
|
d1Encoded = Json.encode(d1)
|
|
d2Encoded = Json.encode(d2)
|
|
d3Encoded = Json.encode(d3)
|
|
|
|
check:
|
|
d1Encoded == $parseJson(data1)
|
|
d2Encoded == $parseJson(data2)
|
|
d3Encoded == $parseJson(data3)
|
|
|
|
except SerializationError as e:
|
|
echo e.getStackTrace
|
|
echo e.formatMsg("<>")
|
|
raise e
|
|
|
|
test "Holders of JsonString":
|
|
testJsonHolders HasJsonString
|
|
|
|
test "Holders of JsonNode":
|
|
testJsonHolders HasJsonNode
|
|
|