173 lines
5.3 KiB
TypeScript
173 lines
5.3 KiB
TypeScript
import { assert, describe, it } from "@effect/vitest"
|
|
import { RpcSerialization } from "effect/unstable/rpc"
|
|
|
|
const responseExitSuccess = (requestId: string | number, value: unknown) => ({
|
|
_tag: "Exit",
|
|
requestId,
|
|
exit: {
|
|
_tag: "Success",
|
|
value
|
|
}
|
|
})
|
|
|
|
describe("RpcSerialization", () => {
|
|
it("json decode keeps array payloads flat", () => {
|
|
const parser = RpcSerialization.json.makeUnsafe()
|
|
const decoded = parser.decode("[1,2,3]")
|
|
assert.strictEqual(decoded.length, 3)
|
|
assert.deepStrictEqual(decoded, [1, 2, 3])
|
|
})
|
|
|
|
it("json decode wraps non-array payloads", () => {
|
|
const parser = RpcSerialization.json.makeUnsafe()
|
|
const decoded = parser.decode("{\"a\":1}")
|
|
assert.strictEqual(decoded.length, 1)
|
|
assert.deepStrictEqual(decoded, [{ a: 1 }])
|
|
})
|
|
|
|
it("jsonRpc encodes a non-batched single response array as an object", () => {
|
|
const parser = RpcSerialization.jsonRpc().makeUnsafe()
|
|
const decoded = parser.decode("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"users.get\"}")
|
|
assert.strictEqual(decoded.length, 1)
|
|
|
|
const encoded = parser.encode([responseExitSuccess("1", { id: 1 })])
|
|
assert(encoded !== undefined)
|
|
|
|
const message = JSON.parse(encoded as string)
|
|
assert.strictEqual(Array.isArray(message), false)
|
|
assert.deepStrictEqual(message, {
|
|
jsonrpc: "2.0",
|
|
id: "1",
|
|
result: {
|
|
id: 1
|
|
}
|
|
})
|
|
})
|
|
|
|
it("jsonRpc encodes batched responses as an array", () => {
|
|
const parser = RpcSerialization.jsonRpc().makeUnsafe()
|
|
const decoded = parser.decode(
|
|
"[{\"jsonrpc\":\"2.0\",\"id\":\"1\",\"method\":\"users.get\"},{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"users.list\"}]"
|
|
)
|
|
assert.strictEqual(decoded.length, 2)
|
|
|
|
const encoded = parser.encode([
|
|
responseExitSuccess("1", "one"),
|
|
responseExitSuccess(2, "two")
|
|
])
|
|
console.log(encoded)
|
|
assert(encoded !== undefined)
|
|
|
|
const message = JSON.parse(encoded as string)
|
|
assert.strictEqual(Array.isArray(message), true)
|
|
assert.deepStrictEqual(message, [
|
|
{
|
|
jsonrpc: "2.0",
|
|
id: "1",
|
|
result: "one"
|
|
},
|
|
{
|
|
jsonrpc: "2.0",
|
|
id: 2,
|
|
result: "two"
|
|
}
|
|
])
|
|
})
|
|
|
|
it("jsonRpc preserves id 0 across decode and encode", () => {
|
|
const parser = RpcSerialization.jsonRpc().makeUnsafe()
|
|
const decoded = parser.decode("{\"jsonrpc\":\"2.0\",\"id\":0,\"method\":\"users.get\"}")
|
|
assert.deepStrictEqual(decoded, [{
|
|
_tag: "Request",
|
|
id: 0,
|
|
tag: "users.get",
|
|
payload: null,
|
|
headers: []
|
|
}])
|
|
|
|
const encoded = parser.encode({
|
|
_tag: "Request",
|
|
id: 0,
|
|
tag: "users.get",
|
|
payload: null,
|
|
headers: []
|
|
})
|
|
assert.strictEqual(
|
|
encoded,
|
|
"{\"jsonrpc\":\"2.0\",\"method\":\"users.get\",\"params\":null,\"id\":0,\"headers\":[]}"
|
|
)
|
|
})
|
|
|
|
it("jsonRpc maps null id to internal notification sentinel", () => {
|
|
const parser = RpcSerialization.jsonRpc().makeUnsafe()
|
|
const decoded = parser.decode("{\"jsonrpc\":\"2.0\",\"id\":null,\"method\":\"users.get\"}")
|
|
assert.deepStrictEqual(decoded, [{
|
|
_tag: "Request",
|
|
id: "",
|
|
tag: "users.get",
|
|
payload: null,
|
|
headers: []
|
|
}])
|
|
})
|
|
|
|
it("jsonRpc preserves empty string id across decode and encode", () => {
|
|
const parser = RpcSerialization.jsonRpc().makeUnsafe()
|
|
const decoded = parser.decode("{\"jsonrpc\":\"2.0\",\"id\":\"\",\"method\":\"users.get\"}")
|
|
assert.deepStrictEqual(decoded, [{
|
|
_tag: "Request",
|
|
id: "",
|
|
tag: "users.get",
|
|
payload: null,
|
|
headers: []
|
|
}])
|
|
|
|
const encoded = parser.encode({
|
|
_tag: "Request",
|
|
id: "",
|
|
tag: "users.get",
|
|
payload: null,
|
|
headers: []
|
|
})
|
|
assert.strictEqual(
|
|
encoded,
|
|
"{\"jsonrpc\":\"2.0\",\"method\":\"users.get\",\"params\":null,\"id\":\"\",\"headers\":[]}"
|
|
)
|
|
})
|
|
|
|
it("msgPack roundtrips an encoded RPC request envelope", () => {
|
|
const parser = RpcSerialization.msgPack.makeUnsafe()
|
|
const payload = { _tag: "Request", id: 1, method: "echo" }
|
|
const encoded = parser.encode(payload)
|
|
const decoded = parser.decode(encoded as Uint8Array)
|
|
assert.strictEqual(decoded.length, 1)
|
|
assert.deepStrictEqual(decoded[0], payload)
|
|
})
|
|
|
|
it("makeMsgPack with useRecords false roundtrips an encoded RPC request envelope", () => {
|
|
const parser = RpcSerialization.makeMsgPack({ useRecords: false }).makeUnsafe()
|
|
const payload = { _tag: "Request", id: 1, method: "echo" }
|
|
const encoded = parser.encode(payload)
|
|
const decoded = parser.decode(encoded as Uint8Array)
|
|
assert.strictEqual(decoded.length, 1)
|
|
assert.deepStrictEqual(decoded[0], payload)
|
|
})
|
|
|
|
it("makeMsgPack with useRecords false handles nested objects with repeated structures", () => {
|
|
const parser = RpcSerialization.makeMsgPack({ useRecords: false }).makeUnsafe()
|
|
const payload = {
|
|
_tag: "Chunk",
|
|
requestId: "1",
|
|
values: [
|
|
responseExitSuccess("1", { _tag: "Ok", data: "a" }),
|
|
responseExitSuccess("2", { _tag: "Ok", data: "b" }),
|
|
responseExitSuccess("3", { _tag: "Ok", data: "c" }),
|
|
responseExitSuccess("4", { _tag: "Ok", data: "d" })
|
|
]
|
|
}
|
|
const encoded = parser.encode(payload)
|
|
const decoded = parser.decode(encoded as Uint8Array)
|
|
assert.strictEqual(decoded.length, 1)
|
|
assert.deepStrictEqual(decoded[0], payload)
|
|
})
|
|
})
|