Skip to content

Commit

Permalink
Tighten int(/long) and float encodable? checks (#85)
Browse files Browse the repository at this point in the history
* Tighten int and float encodable? checks

* Allow `mix format` to run in :dev

* Add typespec
  • Loading branch information
LostKobrakai authored Jan 7, 2023
1 parent bd708fe commit ab11031
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 27 deletions.
13 changes: 10 additions & 3 deletions lib/avro_ex/schema.ex
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,19 @@ defmodule AvroEx.Schema do
encodable?(schema, context, data)
end

@int32_range -2_147_483_648..2_147_483_647
@int64_range -9_223_372_036_854_775_808..9_223_372_036_854_775_807

@spec encodable?(any(), any(), any()) :: boolean()
def encodable?(%Primitive{type: :null}, _, nil), do: true
def encodable?(%Primitive{type: :boolean}, _, bool) when is_boolean(bool), do: true
def encodable?(%Primitive{type: :int}, _, n) when is_integer(n), do: true
def encodable?(%Primitive{type: :long}, _, n) when is_integer(n), do: true
def encodable?(%Primitive{type: :float}, _, n) when is_float(n), do: true
def encodable?(%Primitive{type: :int}, _, n) when is_integer(n) and n in @int32_range, do: true
def encodable?(%Primitive{type: :long}, _, n) when is_integer(n) and n in @int64_range, do: true

def encodable?(%Primitive{type: :float}, _, n) when is_float(n) do
match?(<<^n::little-float-size(32)>>, <<n::little-float-size(32)>>)
end

def encodable?(%Primitive{type: :double}, _, n) when is_float(n), do: true
def encodable?(%Primitive{type: :bytes}, _, bytes) when is_binary(bytes), do: true
def encodable?(%Primitive{type: :string}, _, str) when is_binary(str), do: String.valid?(str)
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ defmodule AvroEx.Mixfile do
{:credo, "~> 1.0", only: :dev, runtime: false},
{:dialyxir, "~> 1.1", only: :dev, runtime: false},
{:ex_doc, "~> 0.20", only: :dev, runtime: false},
{:stream_data, "~> 0.5", only: :test}
{:stream_data, "~> 0.5", only: [:dev, :test]}
]
end

Expand Down
24 changes: 24 additions & 0 deletions test/encode_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,30 @@ defmodule AvroEx.Encode.Test do
assert encoded_union == index <> encoded_int
end

test "works as expected with int and long" do
{:ok, schema} = AvroEx.decode_schema(~S(["int", "long"]))
{:ok, int_schema} = AvroEx.decode_schema(~S("int"))
{:ok, long_schema} = AvroEx.decode_schema(~S("long"))

{:ok, index} = @test_module.encode(int_schema, 1)
{:ok, encoded_long} = @test_module.encode(long_schema, -3_376_656_585_598_455_353)
{:ok, encoded_union} = @test_module.encode(schema, -3_376_656_585_598_455_353)

assert encoded_union == index <> encoded_long
end

test "works as expected with float and double" do
{:ok, schema} = AvroEx.decode_schema(~S(["float", "double"]))
{:ok, int_schema} = AvroEx.decode_schema(~S("int"))
{:ok, double_schema} = AvroEx.decode_schema(~S("double"))

{:ok, index} = @test_module.encode(int_schema, 1)
{:ok, encoded_long} = @test_module.encode(double_schema, 0.0000000001)
{:ok, encoded_union} = @test_module.encode(schema, 0.0000000001)

assert encoded_union == index <> encoded_long
end

test "works as expected with logical types" do
datetime_json = ~S({"type": "long", "logicalType":"timestamp-millis"})
datetime_value = ~U[2020-09-17 12:56:50.438Z]
Expand Down
35 changes: 12 additions & 23 deletions test/property_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,6 @@ defmodule AvroEx.PropertyTest do
use ExUnit.Case, async: true
use ExUnitProperties

test "encoding of large integer as a union of int and long" do
schema = ["int", "long"]
data = -3_376_656_585_598_455_353

json = Jason.encode!(schema)
{:ok, schema} = AvroEx.decode_schema(json)
{:ok, encoded} = AvroEx.encode(schema, data)

assert {:ok, ^data} = AvroEx.decode(schema, encoded)
end

property "encode -> decode always returns back the initial data for the same schema" do
check all schema <- schema(),
data <- valid_data(schema),
Expand All @@ -24,6 +13,7 @@ defmodule AvroEx.PropertyTest do
end
end

@spec schema() :: StreamData.t()
def schema do
sized(fn size -> schema_gen(size) end)
end
Expand Down Expand Up @@ -77,18 +67,17 @@ defmodule AvroEx.PropertyTest do
end

defp union(size) do
schema()
|> resize(div(size, 4))
|> filter(fn schema -> not is_list(schema) end)
|> uniq_list_of(
min_length: 1,
max_length: 8,
uniq_fun: fn
gen all list <-
schema()
|> resize(div(size, 4))
|> filter(fn schema -> not is_list(schema) end)
|> list_of(min_length: 1, max_length: 8) do
Enum.uniq_by(list, fn
%{type: _type, name: name} -> name
%{type: type} -> type
value -> value
end
)
end)
end
end

defp valid_data("null"), do: constant(nil)
Expand All @@ -97,9 +86,9 @@ defmodule AvroEx.PropertyTest do
defp valid_data("long"), do: integer(-9_223_372_036_854_775_808..9_223_372_036_854_775_807)

defp valid_data("float") do
gen all num <- float() do
bin = <<num::float-size(32)>>
<<float::float-size(32)>> = bin
gen all float <- float(),
match?(<<_float::big-float-size(32)>>, <<float::big-float-size(32)>>) do
<<float::big-float-size(32)>> = <<float::big-float-size(32)>>
float
end
end
Expand Down

0 comments on commit ab11031

Please # to comment.