1defmodule MsgpaxTest do
2  use Msgpax.Case, async: true
3
4  doctest Msgpax
5
6  alias Msgpax.PackError
7  alias Msgpax.UnpackError
8
9  defmodule User do
10    @derive [Msgpax.Packer]
11    defstruct [:name]
12  end
13
14  defmodule UserWithAge do
15    @derive [{Msgpax.Packer, fields: [:name]}]
16    defstruct [:name, :age]
17  end
18
19  defmodule UserAllFields do
20    @derive [{Msgpax.Packer, include_struct_field: true}]
21    defstruct [:name]
22  end
23
24  defmodule UserDerivingStructField do
25    @derive [{Msgpax.Packer, fields: [:name, :__struct__]}]
26    defstruct [:name]
27  end
28
29  test "fixstring" do
30    assert_format build_string(0), [160]
31    assert_format build_string(31), [191]
32  end
33
34  test "string 8" do
35    assert_format build_string(32), [217, 32]
36    assert_format build_string(255), [217, 255]
37  end
38
39  test "string 16" do
40    assert_format build_string(0x100), [218, 0x100::16]
41    assert_format build_string(0xFFFF), [218, 0xFFFF::16]
42  end
43
44  test "string 32" do
45    assert_format build_string(0x10000), [219, 0x10000::32]
46  end
47
48  test "binary 8" do
49    assert_format build_bytes(1), [0xC4, 1], build_string(1)
50    assert_format build_bytes(255), [0xC4, 255], build_string(255)
51
52    assert_format build_bytes(1), [0xC4, 1], {build_bytes(1), [binary: true]}
53    assert_format build_bytes(255), [0xC4, 255], {build_bytes(255), [binary: true]}
54  end
55
56  test "binary 16" do
57    assert_format build_bytes(0x100), [0xC5, 0x100::16], build_string(0x100)
58    assert_format build_bytes(0xFFFF), [0xC5, 0xFFFF::16], build_string(0xFFFF)
59  end
60
61  test "binary 32" do
62    assert_format build_bytes(0x10000), [0xC6, 0x10000::32], build_string(0x10000)
63  end
64
65  test "fixarray" do
66    assert_format build_list(0), [144]
67    assert_format build_list(15), [159]
68  end
69
70  test "array 16" do
71    assert_format build_list(16), [220, 16::16]
72    assert_format build_list(0xFFFF), [220, 0xFFFF::16]
73  end
74
75  test "array 32" do
76    assert_format build_list(0x10000), [221, 0x10000::32]
77  end
78
79  test "fixmap" do
80    assert_format build_map(0), [128]
81    assert_format build_map(15), [143]
82  end
83
84  test "map 16" do
85    assert_format build_map(16), [222, 16::16]
86    assert_format build_map(0xFFFF), [222, 0xFFFF::16]
87  end
88
89  test "map 32" do
90    assert_format build_map(0x10000), [223, 0x10000::32]
91  end
92
93  test "booleans" do
94    assert_format false, [194]
95    assert_format true, [195]
96  end
97
98  test "nil" do
99    assert_format nil, [192]
100  end
101
102  test "atoms" do
103    assert_format :ok, [162], "ok"
104    assert_format Atom, [171], "Elixir.Atom"
105  end
106
107  test "float" do
108    assert_format 42.1, [203]
109  end
110
111  test "positive fixint" do
112    assert_format 0, [0]
113    assert_format 127, [127]
114  end
115
116  test "uint 8" do
117    assert_format 128, [204]
118    assert_format 255, [204]
119  end
120
121  test "uint 16" do
122    assert_format 256, [205]
123    assert_format 65535, [205]
124  end
125
126  test "uint 32" do
127    assert_format 65536, [206]
128    assert_format 4294967295, [206]
129  end
130
131  test "uint 64" do
132    assert_format 4294967296, [207]
133  end
134
135  test "negative fixint" do
136    assert_format -1, [255]
137    assert_format -32, [224]
138  end
139
140  test "int 8" do
141    assert_format -33, [208]
142    assert_format -128, [208]
143  end
144
145  test "int 16" do
146    assert_format -129, [209]
147    assert_format -32768, [209]
148  end
149
150  test "int 32" do
151    assert_format -32769, [210]
152    assert_format -2147483648, [210]
153  end
154
155  test "int 64" do
156    assert_format -2147483649, [211]
157  end
158
159  test "complex structures" do
160    data = %{[[123, "foo"], [true]] => [nil, -45, [%{[] => 10.0}]]}
161    assert_format data, [], data
162    assert_format [data], [], [data]
163  end
164
165  test "bitstring" do
166    assert Msgpax.pack([42, <<5::3>>]) == {:error, %PackError{reason: {:not_encodable, <<5::3>>}}}
167  end
168
169  test "too big data" do
170    assert Msgpax.pack([true, -9223372036854775809]) == {:error, %PackError{reason: {:too_big, -9223372036854775809}}}
171  end
172
173  test "pack/2 with the :iodata option" do
174    assert Msgpax.pack([], iodata: true) == {:ok, [144]}
175    assert Msgpax.pack([], iodata: false) == {:ok, <<144>>}
176    assert Msgpax.pack([42, <<5::3>>], iodata: false) == {:error, %PackError{reason: {:not_encodable, <<5::3>>}}}
177  end
178
179  test "pack!/2 with the :iodata option" do
180    assert Msgpax.pack!([], iodata: true) == [144]
181    assert Msgpax.pack!([], iodata: false) == <<144>>
182    assert_raise Msgpax.PackError, fn ->
183      Msgpax.pack!([42, <<5::3>>], iodata: false)
184    end
185  end
186
187  test "excess bytes" do
188    assert Msgpax.unpack(<<255, 1, 2>>) == {:error, %UnpackError{reason: {:excess_bytes, <<1, 2>>}}}
189  end
190
191  test "invalid format" do
192    assert Msgpax.unpack(<<145, 191>>) == {:error, %UnpackError{reason: {:invalid_format, 191}}}
193    assert Msgpax.unpack(<<193, 1>>) == {:error, %UnpackError{reason: {:invalid_format, 193}}}
194  end
195
196  test "incomplete binary" do
197    assert Msgpax.unpack(<<147, 1, 2>>) == {:error, %UnpackError{reason: :incomplete}}
198    assert Msgpax.unpack(<<5::3>>) == {:error, %UnpackError{reason: :incomplete}}
199  end
200
201  test "unpack_slice/1" do
202    assert Msgpax.unpack_slice(<<255, 1>>) == {:ok, -1, <<1>>}
203    assert Msgpax.unpack_slice(<<5::3>>) == {:error, %UnpackError{reason: :incomplete}}
204  end
205
206  test "deriving" do
207    assert Msgpax.pack!(%User{name: "Lex"}) == Msgpax.pack!(%{name: "Lex"})
208
209    assert_raise Protocol.UndefinedError, fn ->
210      Msgpax.pack!(%URI{})
211    end
212
213    assert Msgpax.pack!(%UserWithAge{name: "Luke", age: 9}) == Msgpax.pack!(%{name: "Luke"})
214
215    expected = Msgpax.pack!(%{"__struct__" => UserAllFields, name: "Francine"})
216    assert Msgpax.pack!(%UserAllFields{name: "Francine"}) == expected
217
218    expected = Msgpax.pack!(%{"__struct__" => UserDerivingStructField, name: "Juri"})
219    assert Msgpax.pack!(%UserDerivingStructField{name: "Juri"}) == expected
220  end
221
222  test "timestamp ext" do
223    string =
224      if Version.match?(System.version(), "<= 1.5.1") do
225        "0001-01-01T00:00:00.000000Z"
226      else
227        "0001-01-01T00:00:00.000001Z"
228      end
229    {:ok, datetime, 0} = DateTime.from_iso8601(string)
230    assert_format datetime, []
231
232    {:ok, datetime, 0} = DateTime.from_iso8601("1970-01-01T00:00:00Z")
233    assert_format datetime, []
234
235    datetime = DateTime.utc_now()
236    assert_format datetime, []
237
238    string =
239      if Version.match?(System.version(), "<= 1.5.2") do
240        "9999-12-31T23:59:59.0000000Z"
241      else
242        "9999-12-31T23:59:59.9999999Z"
243      end
244    {:ok, datetime, 0} = DateTime.from_iso8601(string)
245    assert_format datetime, []
246  end
247
248  defp build_string(length) do
249    String.duplicate(".", length)
250  end
251
252  defp build_list(length) do
253    List.duplicate(nil, length)
254  end
255
256  defp build_bytes(size) do
257    size |> build_string() |> Msgpax.Bin.new()
258  end
259
260  defp build_map(0) do
261    %{}
262  end
263
264  defp build_map(size) do
265    {0, true}
266    |> Stream.iterate(fn {index, value} -> {index + 1, value} end)
267    |> Enum.take(size)
268    |> Enum.into(%{})
269  end
270end
271