mirror of
https://github.com/wu736139669/hapi.git
synced 2026-10-08 19:19:42 +00:00
123 lines
4.0 KiB
Swift
123 lines
4.0 KiB
Swift
import Foundation
|
|
import HapiProtocol
|
|
import Testing
|
|
|
|
@Suite("JSONValue wire coding")
|
|
struct JSONValueTests {
|
|
private func decode(_ json: String) throws -> JSONValue {
|
|
try JSONDecoder().decode(JSONValue.self, from: Data(json.utf8))
|
|
}
|
|
|
|
private func encode(_ value: JSONValue) throws -> String {
|
|
let encoder = JSONEncoder()
|
|
encoder.outputFormatting = [.sortedKeys]
|
|
return String(decoding: try encoder.encode(value), as: UTF8.self)
|
|
}
|
|
|
|
@Test func decodesEveryScalarKind() throws {
|
|
#expect(try decode("null") == .null)
|
|
#expect(try decode("true") == .bool(true))
|
|
#expect(try decode("false") == .bool(false))
|
|
#expect(try decode("42") == .number(42))
|
|
#expect(try decode("-7.5") == .number(-7.5))
|
|
#expect(try decode("\"hapi\"") == .string("hapi"))
|
|
}
|
|
|
|
@Test func decodesEmptyContainers() throws {
|
|
#expect(try decode("{}") == .object([:]))
|
|
#expect(try decode("[]") == .array([]))
|
|
}
|
|
|
|
@Test func distinguishesBoolsNumbersAndStrings() throws {
|
|
#expect(try decode("1") == .number(1))
|
|
#expect(try decode("1") != .bool(true))
|
|
#expect(try decode("true") == .bool(true))
|
|
#expect(try decode("true") != .number(1))
|
|
#expect(try decode("\"true\"") == .string("true"))
|
|
#expect(try decode("\"null\"") == .string("null"))
|
|
}
|
|
|
|
@Test func decodesNestedDocument() throws {
|
|
let json = """
|
|
{
|
|
"id": "sess_1",
|
|
"seq": 7,
|
|
"ratio": 0.5,
|
|
"active": true,
|
|
"archived": false,
|
|
"parent": null,
|
|
"tags": ["ios", "m0"],
|
|
"meta": {"nested": {"deep": [1, {"leaf": null}]}}
|
|
}
|
|
"""
|
|
let expected: JSONValue = [
|
|
"id": "sess_1",
|
|
"seq": 7,
|
|
"ratio": 0.5,
|
|
"active": true,
|
|
"archived": false,
|
|
"parent": nil,
|
|
"tags": ["ios", "m0"],
|
|
"meta": ["nested": ["deep": [1, ["leaf": nil]]]],
|
|
]
|
|
#expect(try decode(json) == expected)
|
|
}
|
|
|
|
@Test func roundTripsNestedComposite() throws {
|
|
let original: JSONValue = [
|
|
"role": "agent",
|
|
"content": [
|
|
"type": "output",
|
|
"data": [
|
|
"blocks": [
|
|
["text": "hello", "tokens": 12.5, "done": false],
|
|
["text": "world", "tokens": 3, "done": true],
|
|
],
|
|
"meta": nil,
|
|
"tags": ["a", "b", "c"],
|
|
],
|
|
],
|
|
"seq": 42,
|
|
]
|
|
let data = try JSONEncoder().encode(original)
|
|
let decoded = try JSONDecoder().decode(JSONValue.self, from: data)
|
|
#expect(decoded == original)
|
|
}
|
|
|
|
@Test func roundTripsScalarsAtTopLevel() throws {
|
|
for value in [JSONValue.null, .bool(false), .number(0.25), .string("x")] {
|
|
let data = try JSONEncoder().encode(value)
|
|
let decoded = try JSONDecoder().decode(JSONValue.self, from: data)
|
|
#expect(decoded == value)
|
|
}
|
|
}
|
|
|
|
@Test func encodesDeterministicTextForNonNumericValues() throws {
|
|
// Numbers are excluded here on purpose: their textual form is an
|
|
// implementation detail of JSONEncoder; they are covered by the
|
|
// round-trip tests above.
|
|
let value: JSONValue = [
|
|
"b": true,
|
|
"a": [nil, "x"],
|
|
]
|
|
#expect(try encode(value) == #"{"a":[null,"x"],"b":true}"#)
|
|
}
|
|
|
|
@Test func literalsBuildTheExpectedCases() {
|
|
let object: JSONValue = ["k": 1]
|
|
#expect(object == .object(["k": .number(1)]))
|
|
|
|
let null: JSONValue = nil
|
|
#expect(null == .null)
|
|
|
|
let array: JSONValue = [true, 2.5, "s"]
|
|
#expect(array == .array([.bool(true), .number(2.5), .string("s")]))
|
|
}
|
|
|
|
@Test func rejectsMalformedJSON() {
|
|
#expect(throws: (any Error).self) {
|
|
try JSONDecoder().decode(JSONValue.self, from: Data("{".utf8))
|
|
}
|
|
}
|
|
}
|