Runtime assertions for Ruby
literal.fun
ruby
1# frozen_string_literal: true
2
3require "set"
4
5include Literal::Types
6
7class SerializationPerson < Literal::Data
8 prop :name, String
9 prop :age, Integer
10end
11
12class SerializationEnvelope < Literal::Data
13 prop :id, Integer
14 prop :owner, SerializationPerson
15 prop :tags, _Set(Symbol)
16 prop :metadata, _Hash(Symbol, _Array(_Nilable(_Union(String, Integer))))
17 prop :schedule, _Array(Date)
18 prop :choice, _TaggedUnion(person: SerializationPerson, note: String)
19 prop :payload, _Union(_Hash(Symbol, Integer), _Array(String))
20end
21
22Example = Literal::SerializationContext.new(
23 Literal::StringSerializer,
24 Literal::SymbolSerializer,
25 Literal::IntegerSerializer,
26 Literal::FloatSerializer,
27 Literal::BooleanSerializer,
28 Literal::DateSerializer,
29 Literal::StructureSerializer,
30 Literal::TaggedUnionSerializer,
31 Literal::UnionSerializer,
32 Literal::HashSerializer,
33 Literal::ArraySerializer,
34 Literal::SetSerializer,
35 Literal::NilableSerializer,
36)
37
38test "array serialization roundtrip" do
39 original = [1, 2, 3]
40 type = _Array(Integer)
41 serialized = Example.serialize([1, 2, 3], type:)
42
43 assert_equal(serialized, [1, 2, 3])
44 assert_equal(Example.deserialize(serialized, type:), original)
45end
46
47test "integer serialization roundtrip" do
48 original = 42
49 type = Integer
50 serialized = Example.serialize(42, type:)
51
52 assert_equal(serialized, 42)
53 assert_equal(Example.deserialize(serialized, type:), original)
54end
55
56test "string serialization roundtrip" do
57 original = "example"
58 type = String
59 serialized = Example.serialize(original, type:)
60
61 assert_equal(serialized, "example")
62 assert_equal(Example.deserialize(serialized, type:), original)
63end
64
65test "symbol serialization roundtrip" do
66 original = :example
67 type = Symbol
68 serialized = Example.serialize(:example, type:)
69
70 assert_equal(serialized, "example")
71 assert_equal(Example.deserialize(serialized, type:), original)
72end
73
74test "boolean serialization roundtrip" do
75 original = true
76 type = _Boolean
77 serialized = Example.serialize(true, type:)
78
79 assert_equal(serialized, true)
80 assert_equal(Example.deserialize(serialized, type:), original)
81end
82
83test "date serialization roundtrip" do
84 original = Date.new(2025, 1, 13)
85 type = Date
86 serialized = Example.serialize(original, type:)
87
88 assert_equal(serialized, "2025-01-13")
89 assert_equal(Example.deserialize(serialized, type:), original)
90end
91
92test "float serialization roundtrip" do
93 original = 3.14
94 type = Float
95 serialized = Example.serialize(original, type:)
96
97 assert_equal(serialized, 3.14)
98 assert_equal(Example.deserialize(serialized, type:), original)
99end
100
101test "hash serialization roundtrip" do
102 original = { foo: 1, bar: 2 }
103 type = _Hash(Symbol, Integer)
104 serialized = Example.serialize(original, type:)
105
106 assert_equal(serialized, { "foo" => 1, "bar" => 2 })
107 assert_equal(Example.deserialize(serialized, type:), original)
108end
109
110test "nilable serialization roundtrip" do
111 type = _Nilable(Integer)
112 non_nil = 42
113 non_nil_serialized = Example.serialize(non_nil, type:)
114 nil_serialized = Example.serialize(nil, type:)
115
116 assert_equal(non_nil_serialized, 42)
117 assert_equal(Example.deserialize(non_nil_serialized, type:), non_nil)
118 assert_equal(nil_serialized, nil)
119 assert_equal(Example.deserialize(nil_serialized, type:), nil)
120end
121
122test "set serialization roundtrip" do
123 original = Set[1, 2, 3]
124 type = _Set(Integer)
125 serialized = Example.serialize(original, type:)
126
127 assert_equal(serialized, [1, 2, 3])
128 assert_equal(Example.deserialize(serialized, type:), original)
129end
130
131test "structure serialization roundtrip" do
132 original = SerializationPerson.new(name: "Joel", age: 42)
133 type = SerializationPerson
134 serialized = Example.serialize(original, type:)
135
136 assert_equal(serialized, { "name" => "Joel", "age" => 42 })
137 assert_equal(Example.deserialize(serialized, type:), original)
138end
139
140test "tagged union serialization roundtrip" do
141 type = _TaggedUnion(name: String, age: Integer)
142 name_original = "Joel"
143 age_original = 42
144
145 name_serialized = Example.serialize(name_original, type:)
146 age_serialized = Example.serialize(age_original, type:)
147
148 assert_equal(name_serialized, ["name", "Joel"])
149 assert_equal(age_serialized, ["age", 42])
150 assert_equal(Example.deserialize(name_serialized, type:), name_original)
151 assert_equal(Example.deserialize(age_serialized, type:), age_original)
152end
153
154test "union serialization roundtrip" do
155 type = _Union(String, Integer)
156 name_original = "Joel"
157 age_original = 42
158
159 name_serialized = Example.serialize(name_original, type:)
160 age_serialized = Example.serialize(age_original, type:)
161
162 assert_equal(name_serialized, ["string", "Joel"])
163 assert_equal(age_serialized, ["integer", 42])
164 assert_equal(Example.deserialize(name_serialized, type:), name_original)
165 assert_equal(Example.deserialize(age_serialized, type:), age_original)
166end
167
168test "big nested serialization roundtrip" do
169 original = SerializationEnvelope.new(
170 id: 7,
171 owner: SerializationPerson.new(name: "Joel", age: 42),
172 tags: Set[:admin, :staff],
173 metadata: {
174 primary: ["active", 1, nil],
175 secondary: [2, "backup"],
176 },
177 schedule: [Date.new(2025, 1, 13), Date.new(2025, 1, 20)],
178 choice: SerializationPerson.new(name: "Jill", age: 40),
179 payload: { count: 3, total: 9 },
180 )
181
182 type = SerializationEnvelope
183 serialized = Example.serialize(original, type:)
184
185 assert_equal(serialized, {
186 "id" => 7,
187 "owner" => {
188 "name" => "Joel",
189 "age" => 42,
190 },
191 "tags" => ["admin", "staff"],
192 "metadata" => {
193 "primary" => [["string", "active"], ["integer", 1], nil],
194 "secondary" => [["integer", 2], ["string", "backup"]],
195 },
196 "schedule" => ["2025-01-13", "2025-01-20"],
197 "choice" => ["person", { "name" => "Jill", "age" => 40 }],
198 "payload" => ["hash", { "count" => 3, "total" => 9 }],
199 })
200
201 assert_equal(Example.deserialize(serialized, type:), original)
202end