346 lines
11 KiB
Nim
346 lines
11 KiB
Nim
import std/math
|
|
import std/options
|
|
import std/strformat
|
|
import std/strutils
|
|
import std/unittest
|
|
import pkg/stew/byteutils
|
|
import pkg/stint
|
|
import pkg/codex/contracts/requests
|
|
from pkg/codex/rest/json import RestPurchase
|
|
import pkg/codex/logutils
|
|
import pkg/codex/utils/json as utilsjson
|
|
import pkg/questionable
|
|
import pkg/questionable/results
|
|
import pkg/libp2p
|
|
import ../helpers
|
|
|
|
checksuite "json serialization":
|
|
var request: StorageRequest
|
|
var requestJson: JsonNode
|
|
|
|
func flatten(s: string): string =
|
|
s.replace(" ")
|
|
.replace("\n")
|
|
|
|
setup:
|
|
request = StorageRequest(
|
|
client: Address.init("0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174").get(),
|
|
ask: StorageAsk(
|
|
slots: 4,
|
|
slotSize: (1 * 1024 * 1024 * 1024).u256, # 1 Gigabyte
|
|
duration: (10 * 60 * 60).u256, # 10 hours
|
|
collateral: 200.u256,
|
|
proofProbability: 4.u256, # require a proof roughly once every 4 periods
|
|
reward: 84.u256,
|
|
maxSlotLoss: 2 # 2 slots can be freed without data considered to be lost
|
|
),
|
|
content: StorageContent(
|
|
cid: "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob",
|
|
merkleRoot: array[32, byte].fromHex("0xc066dd7e405de5a795ce1e765209cfaa6de6c74829c607d1a2fe53107107a298")
|
|
),
|
|
expiry: 1691545330.u256,
|
|
nonce: Nonce array[32, byte].fromHex("0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1")
|
|
)
|
|
requestJson = """{
|
|
"client": "0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174",
|
|
"ask": {
|
|
"slots": 4,
|
|
"slotSize": "1073741824",
|
|
"duration": "36000",
|
|
"proofProbability": "4",
|
|
"reward": "84",
|
|
"collateral": "200",
|
|
"maxSlotLoss": 2
|
|
},
|
|
"content": {
|
|
"cid": "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob",
|
|
"merkleRoot": "0xc066dd7e405de5a795ce1e765209cfaa6de6c74829c607d1a2fe53107107a298"
|
|
},
|
|
"expiry": "1691545330",
|
|
"nonce": "0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1"
|
|
}""".parseJson
|
|
|
|
test "serializes UInt256 to non-hex string representation":
|
|
check (% 100000.u256) == newJString("100000")
|
|
|
|
test "serializes sequence to an array":
|
|
let json = % @[1, 2, 3]
|
|
let expected = "[1,2,3]"
|
|
check $json == expected
|
|
|
|
test "serializes Option[T] when has a value":
|
|
let obj = %(some 1)
|
|
let expected = "1"
|
|
check $obj == expected
|
|
|
|
test "serializes Option[T] when doesn't have a value":
|
|
let obj = %(none int)
|
|
let expected = "null"
|
|
check $obj == expected
|
|
|
|
test "serializes uints int.high or smaller":
|
|
let largeUInt: uint = uint(int.high)
|
|
check %largeUInt == newJInt(BiggestInt(largeUInt))
|
|
|
|
test "serializes large uints":
|
|
let largeUInt: uint = uint(int.high) + 1'u
|
|
check %largeUInt == newJString($largeUInt)
|
|
|
|
|
|
test "serializes Inf float":
|
|
check %Inf == newJString("inf")
|
|
|
|
test "serializes -Inf float":
|
|
check %(-Inf) == newJString("-inf")
|
|
|
|
test "deserializes NaN float":
|
|
check %NaN == newJString("nan")
|
|
|
|
test "can construct json objects with %*":
|
|
type MyObj = object
|
|
mystring {.serialize.}: string
|
|
myint {.serialize.}: int
|
|
myoption {.serialize.}: ?bool
|
|
|
|
let myobj = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
|
let mystuint = 100000.u256
|
|
|
|
let json = %*{
|
|
"myobj": myobj,
|
|
"mystuint": mystuint
|
|
}
|
|
|
|
let expected = """{
|
|
"myobj": {
|
|
"mystring": "abc",
|
|
"myint": 123,
|
|
"myoption": true
|
|
},
|
|
"mystuint": "100000"
|
|
}""".flatten
|
|
|
|
check $json == expected
|
|
|
|
test "only serializes marked fields":
|
|
type MyObj = object
|
|
mystring {.serialize.}: string
|
|
myint {.serialize.}: int
|
|
mybool: bool
|
|
|
|
let obj = % MyObj(mystring: "abc", myint: 1, mybool: true)
|
|
|
|
let expected = """{
|
|
"mystring": "abc",
|
|
"myint": 1
|
|
}""".flatten
|
|
|
|
check $obj == expected
|
|
|
|
test "serializes ref objects":
|
|
type MyRef = ref object
|
|
mystring {.serialize.}: string
|
|
myint {.serialize.}: int
|
|
|
|
let obj = % MyRef(mystring: "abc", myint: 1)
|
|
|
|
let expected = """{
|
|
"mystring": "abc",
|
|
"myint": 1
|
|
}""".flatten
|
|
|
|
check $obj == expected
|
|
|
|
test "serializes RestPurchase":
|
|
let request = % RestPurchase(
|
|
request: some request,
|
|
requestId: RequestId.fromHex("0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1"),
|
|
error: some "error",
|
|
state: "state"
|
|
)
|
|
let expected = """{
|
|
"requestId": "0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1",
|
|
"request": {
|
|
"client": "0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174",
|
|
"ask": {
|
|
"slots": 4,
|
|
"slotSize": "1073741824",
|
|
"duration": "36000",
|
|
"proofProbability": "4",
|
|
"reward": "84",
|
|
"collateral": "200",
|
|
"maxSlotLoss": 2
|
|
},
|
|
"content": {
|
|
"cid": "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"
|
|
},
|
|
"expiry": "1691545330"
|
|
},
|
|
"state": "state",
|
|
"error": "error"
|
|
}""".flatten
|
|
check $request == expected
|
|
|
|
test "serializes StorageRequest":
|
|
let expected = """{
|
|
"client": "0xebcb2b4c2e3c9105b1a53cd128c5ed17c3195174",
|
|
"ask": {
|
|
"slots": 4,
|
|
"slotSize": "1073741824",
|
|
"duration": "36000",
|
|
"proofProbability": "4",
|
|
"reward": "84",
|
|
"collateral": "200",
|
|
"maxSlotLoss": 2
|
|
},
|
|
"content": {
|
|
"cid": "zb2rhheVmk3bLks5MgzTqyznLu1zqGH5jrfTA1eAZXrjx7Vob"
|
|
},
|
|
"expiry": "1691545330"
|
|
}""".flatten
|
|
check request.toJson == expected
|
|
|
|
test "deserialize enum":
|
|
let json = newJString($CidVersion.CIDv1)
|
|
check !CidVersion.fromJson(json) == CIDv1
|
|
|
|
test "deserializes UInt256 from non-hex string representation":
|
|
let json = newJString("100000")
|
|
check !UInt256.fromJson(json) == 100000.u256
|
|
|
|
test "deserializes Option[T] when has a value":
|
|
let json = newJInt(1)
|
|
check (!fromJson(?int, json) == some 1)
|
|
|
|
test "deserializes Option[T] when doesn't have a value":
|
|
let json = newJNull()
|
|
check !fromJson(?int, json) == none int
|
|
|
|
test "deserializes float":
|
|
let json = newJFloat(1.234)
|
|
check !float.fromJson(json) == 1.234
|
|
|
|
test "deserializes Inf float":
|
|
let json = newJString("inf")
|
|
check !float.fromJson(json) == Inf
|
|
|
|
test "deserializes -Inf float":
|
|
let json = newJString("-inf")
|
|
check !float.fromJson(json) == -Inf
|
|
|
|
test "deserializes NaN float":
|
|
let json = newJString("nan")
|
|
check float.fromJson(json).get.isNaN
|
|
|
|
test "deserializes array to sequence":
|
|
let expected = @[1, 2, 3]
|
|
let json = "[1,2,3]".parseJson
|
|
check !seq[int].fromJson(json) == expected
|
|
|
|
test "deserializes uints int.high or smaller":
|
|
let largeUInt: uint = uint(int.high)
|
|
let json = newJInt(BiggestInt(largeUInt))
|
|
check !uint.fromJson(json) == largeUInt
|
|
|
|
test "deserializes large uints":
|
|
let largeUInt: uint = uint(int.high) + 1'u
|
|
let json = newJString($BiggestUInt(largeUInt))
|
|
check !uint.fromJson(json) == largeUInt
|
|
|
|
test "can deserialize json objects":
|
|
type MyObj = object
|
|
mystring: string
|
|
myint: int
|
|
myoption: ?bool
|
|
|
|
let expected = MyObj(mystring: "abc", myint: 123, myoption: some true)
|
|
|
|
let json = parseJson("""{
|
|
"mystring": "abc",
|
|
"myint": 123,
|
|
"myoption": true
|
|
}""")
|
|
check !MyObj.fromJson(json) == expected
|
|
|
|
test "ignores serialize pragma when deserializing":
|
|
type MyObj = object
|
|
mystring {.serialize.}: string
|
|
mybool: bool
|
|
|
|
let expected = MyObj(mystring: "abc", mybool: true)
|
|
|
|
let json = parseJson("""{
|
|
"mystring": "abc",
|
|
"mybool": true
|
|
}""")
|
|
|
|
check !MyObj.fromJson(json) == expected
|
|
|
|
test "deserializes objects with extra fields":
|
|
type MyObj = object
|
|
mystring: string
|
|
mybool: bool
|
|
|
|
let expected = MyObj(mystring: "abc", mybool: true)
|
|
|
|
let json = """{
|
|
"mystring": "abc",
|
|
"mybool": true,
|
|
"extra": "extra"
|
|
}""".parseJson
|
|
check !MyObj.fromJson(json) == expected
|
|
|
|
test "deserializes objects with less fields":
|
|
type MyObj = object
|
|
mystring: string
|
|
mybool: bool
|
|
|
|
let expected = MyObj(mystring: "abc", mybool: false)
|
|
|
|
let json = """{
|
|
"mystring": "abc"
|
|
}""".parseJson
|
|
check !MyObj.fromJson(json) == expected
|
|
|
|
test "deserializes ref objects":
|
|
type MyRef = ref object
|
|
mystring: string
|
|
myint: int
|
|
|
|
let expected = MyRef(mystring: "abc", myint: 1)
|
|
|
|
let json = """{
|
|
"mystring": "abc",
|
|
"myint": 1
|
|
}""".parseJson
|
|
|
|
let deserialized = !MyRef.fromJson(json)
|
|
check deserialized.mystring == expected.mystring
|
|
check deserialized.myint == expected.myint
|
|
|
|
test "deserializes Cid":
|
|
let
|
|
jCid = newJString("zdj7Wakya26ggQWkvMdHYFcPgZ7Qh2HdMooQDDFDHkk4uHS14")
|
|
cid = "zdj7Wakya26ggQWkvMdHYFcPgZ7Qh2HdMooQDDFDHkk4uHS14"
|
|
|
|
check:
|
|
!Cid.fromJson(jCid) == !Cid.init(cid).mapFailure
|
|
|
|
test "deserializes StorageRequest":
|
|
check !StorageRequest.fromJson(requestJson) == request
|
|
|
|
test "deserializes RestPurchase":
|
|
let json = """{
|
|
"requestId": "0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1",
|
|
"state": "state",
|
|
"error": "error"
|
|
}""".parseJson
|
|
json["request"] = requestJson
|
|
|
|
let expected = RestPurchase(
|
|
requestId: RequestId.fromHex("0xd4ebeadc44641c0a271153f6366f24ebb5e3aa64f9ee5e62794babc2e75950a1"),
|
|
state: "state",
|
|
error: some "error",
|
|
request: some request
|
|
)
|
|
check !RestPurchase.fromJson(json) == expected
|