Skip to content

Commit 3147b28

Browse files
committed
Add a lot of tests
1 parent cd67970 commit 3147b28

File tree

6 files changed

+318
-3
lines changed

6 files changed

+318
-3
lines changed

coveralls.json

+3-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"test",
66
"generated",
77
"conformance",
8-
"lib/google/compiler/plugin.pb.ex",
9-
"lib/google/descriptor.pb.ex"
8+
"lib/elixirpb.pb.ex",
9+
"lib/google/protobuf/compiler/plugin.pb.ex",
10+
"lib/google/protobuf/descriptor.pb.ex"
1011
]
1112
}

test/protobuf/decoder_test.exs

+8
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,14 @@ defmodule Protobuf.DecoderTest do
203203
%TestMsg.ContainsTransformModule{field: nil}
204204
end
205205

206+
test "raises an error on unknown wire types" do
207+
payload = Protobuf.Encoder.encode_fnum(_fnum = 1, _wire_type = 6)
208+
209+
assert_raise Protobuf.DecodeError, "cannot decode binary data, unknown wire type: 6", fn ->
210+
Decoder.decode(payload, TestMsg.Oneof)
211+
end
212+
end
213+
206214
describe "groups" do
207215
test "skips all groups and their fields" do
208216
a = <<8, 42>>

test/protobuf/dsl_test.exs

+13
Original file line numberDiff line numberDiff line change
@@ -299,4 +299,17 @@ defmodule Protobuf.DSLTest do
299299
)
300300
end
301301
end
302+
303+
test "raises a compilation error if syntax is proto3 and the first enum has tag other than 0" do
304+
assert_raise RuntimeError, "the first enum value must have tag 0 in proto3, got: 1", fn ->
305+
Code.eval_quoted(
306+
quote do
307+
defmodule MessageWithProto3BadEnumTag do
308+
use Protobuf, syntax: :proto3, enum: true
309+
field :NOT_ZERO, 1
310+
end
311+
end
312+
)
313+
end
314+
end
302315
end

test/protobuf/json/decode_test.exs

+105-1
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,30 @@ defmodule Protobuf.JSON.DecodeTest do
681681
)}
682682
end
683683

684+
test "Google.Protobuf.FloatValue" do
685+
data = %{
686+
"optionalFloatWrapper" => 3.14
687+
}
688+
689+
assert decode(data, TestAllTypesProto3) ==
690+
{:ok,
691+
TestAllTypesProto3.new(
692+
optional_float_wrapper: Google.Protobuf.FloatValue.new!(value: 3.14)
693+
)}
694+
end
695+
696+
test "Google.Protobuf.DoubleValue" do
697+
data = %{
698+
"optionalDoubleWrapper" => 3.14444
699+
}
700+
701+
assert decode(data, TestAllTypesProto3) ==
702+
{:ok,
703+
TestAllTypesProto3.new(
704+
optional_double_wrapper: Google.Protobuf.DoubleValue.new!(value: 3.14444)
705+
)}
706+
end
707+
684708
test "Google.Protobuf.StringValue" do
685709
data = %{
686710
"optionalStringWrapper" => "my string"
@@ -689,7 +713,87 @@ defmodule Protobuf.JSON.DecodeTest do
689713
assert decode(data, TestAllTypesProto3) ==
690714
{:ok,
691715
TestAllTypesProto3.new(
692-
optional_string_wrapper: Google.Protobuf.UInt64Value.new!(value: "my string")
716+
optional_string_wrapper: Google.Protobuf.StringValue.new!(value: "my string")
717+
)}
718+
end
719+
720+
test "Google.Protobuf.BytesValue" do
721+
data = %{
722+
"optionalBytesWrapper" => Base.url_encode64(<<1, 2, 3>>)
723+
}
724+
725+
assert decode(data, TestAllTypesProto3) ==
726+
{:ok,
727+
TestAllTypesProto3.new(
728+
optional_bytes_wrapper: Google.Protobuf.BytesValue.new!(value: <<1, 2, 3>>)
729+
)}
730+
end
731+
732+
test "Google.Protobuf.FieldMask with empty field mask" do
733+
cases = [
734+
{"", []},
735+
{"helloWorld1", ["hello_world1"]},
736+
{"fooBar,baz2Bong", ["foo_bar", "baz2_bong"]}
737+
]
738+
739+
for {json, expected_paths} <- cases do
740+
data = %{"optionalFieldMask" => json}
741+
742+
assert decode(data, TestAllTypesProto3) ==
743+
{:ok,
744+
TestAllTypesProto3.new(
745+
optional_field_mask: Google.Protobuf.FieldMask.new!(paths: expected_paths)
746+
)}
747+
end
748+
749+
# Error with bad type.
750+
data = %{"optionalFieldMask" => 1}
751+
assert {:error, %Protobuf.JSON.DecodeError{} = error} = decode(data, TestAllTypesProto3)
752+
753+
assert Exception.message(error) ==
754+
"JSON map expected for module Google.Protobuf.FieldMask, got: 1"
755+
756+
# Error with bad mask.
757+
data = %{"optionalFieldMask" => "goodMask,badmask!"}
758+
assert {:error, %Protobuf.JSON.DecodeError{} = error} = decode(data, TestAllTypesProto3)
759+
assert Exception.message(error) == ~s(invalid characters in field mask: "badmask!")
760+
end
761+
762+
test "Google.Protobuf.ListValue" do
763+
data = %{
764+
"repeatedListValue" => [[1, "two", 3.14, true, nil], [%{"foo" => "bar"}, []]]
765+
}
766+
767+
assert decode(data, TestAllTypesProto3) ==
768+
{:ok,
769+
TestAllTypesProto3.new(
770+
repeated_list_value: [
771+
Google.Protobuf.ListValue.new!(
772+
values: [
773+
Google.Protobuf.Value.new!(kind: {:number_value, 1.0}),
774+
Google.Protobuf.Value.new!(kind: {:string_value, "two"}),
775+
Google.Protobuf.Value.new!(kind: {:number_value, 3.14}),
776+
Google.Protobuf.Value.new!(kind: {:bool_value, true}),
777+
Google.Protobuf.Value.new!(kind: {:null_value, :NULL_VALUE})
778+
]
779+
),
780+
Google.Protobuf.ListValue.new!(
781+
values: [
782+
Google.Protobuf.Value.new!(
783+
kind:
784+
{:struct_value,
785+
Google.Protobuf.Struct.new!(
786+
fields: %{
787+
"foo" => Google.Protobuf.Value.new!(kind: {:string_value, "bar"})
788+
}
789+
)}
790+
),
791+
Google.Protobuf.Value.new!(
792+
kind: {:list_value, Google.Protobuf.ListValue.new!(values: [])}
793+
)
794+
]
795+
)
796+
]
693797
)}
694798
end
695799

test/protobuf/json/encode_test.exs

+140
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,102 @@ defmodule Protobuf.JSON.EncodeTest do
238238
assert encode(message, emit_unpopulated: true) == %{decoded | "h" => [bar, bar]}
239239
end
240240

241+
describe "Google.Protobuf.* value wrappers" do
242+
test "Google.Protobuf.BoolValue" do
243+
message =
244+
TestAllTypesProto3.new!(
245+
optional_bool_wrapper: Google.Protobuf.BoolValue.new!(value: true)
246+
)
247+
248+
assert encode(message) == %{"optionalBoolWrapper" => true}
249+
end
250+
251+
test "Google.Protobuf.StringValue" do
252+
message =
253+
TestAllTypesProto3.new!(
254+
optional_string_wrapper: Google.Protobuf.StringValue.new!(value: "foo")
255+
)
256+
257+
assert encode(message) == %{"optionalStringWrapper" => "foo"}
258+
end
259+
260+
test "Google.Protobuf.FloatValue" do
261+
message =
262+
TestAllTypesProto3.new!(
263+
optional_float_wrapper: Google.Protobuf.FloatValue.new!(value: 3.14)
264+
)
265+
266+
assert encode(message) == %{"optionalFloatWrapper" => 3.14}
267+
end
268+
269+
test "Google.Protobuf.DoubleValue" do
270+
message =
271+
TestAllTypesProto3.new!(
272+
optional_double_wrapper: Google.Protobuf.DoubleValue.new!(value: 3.14)
273+
)
274+
275+
assert encode(message) == %{"optionalDoubleWrapper" => 3.14}
276+
end
277+
278+
test "Google.Protobuf.Int32Value" do
279+
message =
280+
TestAllTypesProto3.new!(
281+
optional_int32_wrapper: Google.Protobuf.Int32Value.new!(value: -3)
282+
)
283+
284+
assert encode(message) == %{"optionalInt32Wrapper" => -3}
285+
end
286+
287+
test "Google.Protobuf.UInt32Value" do
288+
message =
289+
TestAllTypesProto3.new!(
290+
optional_uint32_wrapper: Google.Protobuf.UInt32Value.new!(value: 3)
291+
)
292+
293+
assert encode(message) == %{"optionalUint32Wrapper" => 3}
294+
end
295+
296+
test "Google.Protobuf.Int64Value" do
297+
message =
298+
TestAllTypesProto3.new!(
299+
optional_int64_wrapper: Google.Protobuf.Int64Value.new!(value: -3)
300+
)
301+
302+
assert encode(message) == %{"optionalInt64Wrapper" => -3}
303+
end
304+
305+
test "Google.Protobuf.UInt64Value" do
306+
message =
307+
TestAllTypesProto3.new!(
308+
optional_uint64_wrapper: Google.Protobuf.UInt64Value.new!(value: 3)
309+
)
310+
311+
assert encode(message) == %{"optionalUint64Wrapper" => 3}
312+
end
313+
end
314+
315+
describe "Google.Protobuf.BytesValue" do
316+
test "encodes with base 64" do
317+
message =
318+
TestAllTypesProto3.new!(
319+
optional_bytes_wrapper: Google.Protobuf.BytesValue.new!(value: <<1, 2, 3>>)
320+
)
321+
322+
assert encode(message) == %{"optionalBytesWrapper" => Base.url_encode64(<<1, 2, 3>>)}
323+
end
324+
end
325+
326+
describe "Google.Protobuf.FieldMask" do
327+
test "encodes with base 64" do
328+
message =
329+
TestAllTypesProto3.new!(
330+
optional_field_mask: Google.Protobuf.FieldMask.new!(paths: ["foo_bar", "baz_bong"])
331+
)
332+
333+
assert encode(message) == %{"optionalFieldMask" => "fooBar,bazBong"}
334+
end
335+
end
336+
241337
describe "Google.Protobuf.Duration" do
242338
test "encodes as a string" do
243339
cases = [
@@ -284,6 +380,16 @@ defmodule Protobuf.JSON.EncodeTest do
284380
end
285381

286382
describe "Google.Protobuf.Timestamp" do
383+
test "encodes the timestamp as a string" do
384+
{:ok, datetime, _offset = 0} = DateTime.from_iso8601("2000-01-01T00:00:00Z")
385+
unix_seconds = DateTime.to_unix(datetime, :second)
386+
387+
timestamp = Google.Protobuf.Timestamp.new!(seconds: unix_seconds, nanos: 0)
388+
message = TestAllTypesProto3.new!(optional_timestamp: timestamp)
389+
390+
assert encode(message) == %{"optionalTimestamp" => "2000-01-01T00:00:00Z"}
391+
end
392+
287393
test "returns an error if the timestamp is outside of the allowed range" do
288394
{:ok, datetime, _offset = 0} = DateTime.from_iso8601("0000-01-01T00:00:00Z")
289395
unix_seconds = DateTime.to_unix(datetime, :second)
@@ -295,4 +401,38 @@ defmodule Protobuf.JSON.EncodeTest do
295401
{:invalid_timestamp, timestamp, "timestamp is outside of allowed range"}
296402
end
297403
end
404+
405+
describe "Google.Protobuf.Value" do
406+
test "encodes correctly" do
407+
cases = [
408+
{{:string_value, "foo"}, "foo"},
409+
{{:bool_value, true}, true},
410+
{{:number_value, 3.14}, 3.14},
411+
{{:number_value, 1}, 1},
412+
{{:null_value, :NULL_VALUE}, nil}
413+
]
414+
415+
for {kind, json} <- cases do
416+
value = Google.Protobuf.Value.new!(kind: kind)
417+
message = TestAllTypesProto3.new!(optional_value: value)
418+
assert encode(message) == %{"optionalValue" => json}
419+
end
420+
end
421+
end
422+
423+
describe "Google.Protobuf.ListValue" do
424+
test "encodes correctly" do
425+
value = Google.Protobuf.ListValue.new!(values: [])
426+
message = TestAllTypesProto3.new!(repeated_list_value: [value])
427+
assert encode(message) == %{"repeatedListValue" => [[]]}
428+
end
429+
end
430+
431+
describe "Google.Protobuf.Struct" do
432+
test "encodes correctly" do
433+
value = Google.Protobuf.Struct.new!(fields: %{"foo" => Google.Protobuf.Empty.new!([])})
434+
message = TestAllTypesProto3.new!(optional_struct: value)
435+
assert encode(message) == %{"optionalStruct" => %{"foo" => %{}}}
436+
end
437+
end
298438
end

test/protobuf/json/rfc3339_test.exs

+49
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,50 @@ defmodule Protobuf.JSON.RFC3339Test do
1414
assert nanos == 310_017_000
1515
end
1616

17+
test "returns {:ok, seconds, nanos} with a time offset" do
18+
assert {:ok, seconds, nanos} = RFC3339.decode("2021-11-26T16:19:13.310017+01:00")
19+
20+
{:ok, dt, _offset} = DateTime.from_iso8601("2021-11-26T16:19:13+01:00")
21+
22+
assert seconds == DateTime.to_unix(dt, :second)
23+
assert nanos == 310_017_000
24+
end
25+
1726
test "returns {:error, reason} if the timestamp is outside of the allowed range" do
1827
assert {:error, reason} = RFC3339.decode("0000-01-01T00:00:00Z")
1928
assert reason == "timestamp is outside of allowed range"
2029
end
2130

31+
test "returns {:error, reason} if the timestamp has cruft after" do
32+
assert {:error, reason} = RFC3339.decode("0000-01-01T00:00:00Zand the rest")
33+
assert reason == ~s(expected empty string, got: "and the rest")
34+
end
35+
36+
test "returns {:error, reason} if the timestamp is missing the offset" do
37+
assert {:error, reason} = RFC3339.decode("0000-01-01T00:00:00")
38+
assert reason == "expected time offset, but it's missing"
39+
end
40+
41+
test "returns {:error, reason} with bad nanoseconds" do
42+
assert {:error, reason} = RFC3339.decode("0000-01-01T00:00:00.nonanoZ")
43+
assert reason == ~s(bad time secfrac after ".", got: "nonanoZ")
44+
end
45+
46+
test "returns {:error, reason} with bad digit something" do
47+
assert {:error, reason} = RFC3339.decode("000-01-01T00:00:00Z")
48+
assert reason == ~s(expected 4 digits but got unparsable integer: "000-")
49+
end
50+
51+
test "returns {:error, reason} with missing T to separate date and time" do
52+
assert {:error, reason} = RFC3339.decode("0000-01-01.00:00:00Z")
53+
assert reason == ~s(expected literal "T", got: ".00:00:00Z")
54+
end
55+
56+
test "returns {:error, reason} with not enough digits" do
57+
assert {:error, reason} = RFC3339.decode("000")
58+
assert reason == ~s(expected 4 digits, got: "000")
59+
end
60+
2261
test "returns {:ok, seconds, nanos} for the latest possible timestamp" do
2362
assert {:ok, _seconds, 999_999_999} = RFC3339.decode("9999-12-31T23:59:59.999999999Z")
2463
end
@@ -45,6 +84,16 @@ defmodule Protobuf.JSON.RFC3339Test do
4584
assert RFC3339.encode(unix_sec, 123_000) == {:ok, "2021-11-26T16:19:13.000123Z"}
4685
end
4786

87+
test "returns {:error, reason} with too big nanos" do
88+
assert {:error, message} = RFC3339.encode(_unix_sec = 0, _nanos = 10_000_000_000)
89+
assert message == "nanos can't be bigger than 1000000000, got: 10000000000"
90+
end
91+
92+
test "returns {:error, reason} with invalid seconds" do
93+
assert {:error, message} = RFC3339.encode(10_000_000_000_000_000, _nanos = 0)
94+
assert message == ":invalid_unix_time"
95+
end
96+
4897
property "injects the right nanoseconds regardless of how many digits" do
4998
check all digits_count <- member_of([3, 6, 9]),
5099
max_range = String.to_integer(String.duplicate("9", digits_count)),

0 commit comments

Comments
 (0)