Skip to content

Commit

Permalink
Write test suite
Browse files Browse the repository at this point in the history
  • Loading branch information
hauleth committed Oct 3, 2018
1 parent 7d941e4 commit cee2b43
Show file tree
Hide file tree
Showing 17 changed files with 361 additions and 23 deletions.
3 changes: 2 additions & 1 deletion coveralls.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"skip_files": [
"test/support"
"test/support",
"lib/imager_web/instrumenter.ex"
],

"coverage_options": {
Expand Down
20 changes: 18 additions & 2 deletions lib/imager/config/port_normalization.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,23 @@ defmodule Imager.Config.PortNormalization do
Normalize port values to integers
"""

def transform(:port, value) when is_binary(value), do: String.to_integer(value)
def transform(:port, value) when is_integer(value), do: value
def transform(:port, value) when is_binary(value) do
num = String.to_integer(value)

if num > 0 do
num
else
raise "Expected port to be positive integer, got: #{inspect(value)}"
end
rescue
ArgumentError ->
raise "Expected port to be positive integer, got: #{inspect(value)}"
end

def transform(:port, value) when value > 0, do: value

def transform(:port, value),
do: raise("Expected port to be positive integer, got: #{inspect(value)}")

def transform(_, value), do: value
end
39 changes: 22 additions & 17 deletions lib/imager/config/store_transform.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule Imager.Config.StoreTransform do
require Logger

@reserved ~w[health]
@types ~w[S3 Local Blackhole]

def transform(:stores, entries) do
Map.new(entries, fn {path, values} ->
Expand All @@ -24,26 +25,24 @@ defmodule Imager.Config.StoreTransform do
cache: cache
}}
else
_ -> throw({:invalid, path, entries})
end
end)
catch
{:invalid, path, entries} ->
raise """
Invalid store `#{path}` definition, store needs to be defined in form
_ ->
raise """
Invalid store `#{path}` definition, store needs to be defined in form
[stores.path]
type = {"S3" or "Local"}
[stores.path]
type = {#{list()}
or
or
[stores.path.store]
type = {"S3" or "Local"}
[stores.path.cache]
type = {"S3" or "Local"}
[stores.path.store]
type = {#{list()}
[stores.path.cache]
type = {#{list()}}
Got #{inspect entries}
"""
Got #{inspect entries}
"""
end
end)
end

def transform(_k, value), do: value
Expand All @@ -54,12 +53,18 @@ defmodule Imager.Config.StoreTransform do
defp get_cache(%{cache: values}), do: parse(values)
defp get_cache(values), do: parse(values)

defp parse(%{type: type} = values) when type in ~w(S3 Local Blackhole) do
defp parse(%{type: type} = values) when type in @types do
module = Module.safe_concat(Imager.Store, type)
opts = Keyword.new(Map.get(values, :options, []))

{:ok, {module, opts}}
end

defp parse(values), do: {:error, values}

defp list do
[last | rest] = @types

Enum.join(rest, ", ") <> " or " <> last
end
end
2 changes: 1 addition & 1 deletion lib/imager_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ defmodule ImagerWeb.Endpoint do
"""
def init(_key, config) do
port =
Application.get_env(:imager, :port) || System.get_env("PORT") ||
System.get_env("PORT") || Application.get_env(:imager, :port) ||
raise "expected the PORT environment variable to be set"

{:ok, Keyword.put(config, :http, [:inet6, port: port])}
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ defmodule Imager.Mixfile do
{:dialyxir, ">= 0.0.0", only: [:dev], runtime: false},
{:junit_formatter, "~> 2.2", only: [:test]},
{:excoveralls, "~> 0.10", only: [:test]},
{:stream_data, "~> 0.1", only: [:test]}
{:stream_data, "~> 0.1", only: [:test]},
{:temp, "~> 0.4", only: [:test]}
]
end

Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/correct.config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[stores.test]
type = "Blackhole"
Empty file added test/fixtures/file-without-ext
Empty file.
1 change: 1 addition & 0 deletions test/fixtures/incorrect.config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[foo
31 changes: 31 additions & 0 deletions test/imager/config/env_transform_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule Imager.Config.EnvTransformTest do
use ExUnit.Case, async: true
use ExUnitProperties

alias Imager.Config.EnvTransform, as: Subject

defp non_env() do
gen all value <- term(),
not match?("$" <> _, value),
do: value
end

property "values not starting with `$` are passed as is" do
check all key <- atom(:alphanumeric),
value <- non_env() do
assert value == Subject.transform(key, value)
end
end

test "returns set value when there exists environment variable" do
System.put_env("FOO", "foo")

on_exit fn -> System.delete_env("FOO") end

assert "foo" == Subject.transform(:foo, "$FOO")
end

test "returns nil when value is unset" do
assert is_nil Subject.transform(:foo, "$NON_SET_ENV_VAR")
end
end
61 changes: 61 additions & 0 deletions test/imager/config/port_normalization_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
defmodule Imager.Config.PortNormalizationTest do
use ExUnit.Case, async: true
use ExUnitProperties

alias Imager.Config.PortNormalization, as: Subject

defp non_positive_integer() do
gen(all value <- one_of([positive_integer(), constant(0)]), do: -value)
end

defp assert_fail(value) do
assert_raise RuntimeError,
"Expected port to be positive integer, got: #{inspect(value)}",
fn ->
Subject.transform(:port, value)
end
end

property "port values of positive integer are passed through" do
check all value <- positive_integer() do
assert value == Subject.transform(:port, value)
end
end

property "port values of strings representing positive integers are passed through" do
check all num <- positive_integer(),
value = Integer.to_string(num) do
assert num == Subject.transform(:port, value)
end
end

property "raises error for non-positive integers" do
check all value <- non_positive_integer() do
assert_fail(value)
end
end

property "raises error for strings containing non-positive integers" do
check all num <- non_positive_integer(),
value = Integer.to_string(num) do
assert_fail(value)
end
end

property "raises error for strings not containing numbers" do
check all number <- string(?0..?9),
alphas <- string(?a..?z, min_length: 1) do
assert_fail(alphas <> number)
assert_fail(number <> alphas)
assert_fail(alphas)
end
end

property "all non-port fields are passed through" do
check all key <- atom(:alphanumeric),
key != :port,
value <- term() do
assert value == Subject.transform(key, value)
end
end
end
90 changes: 90 additions & 0 deletions test/imager/config/store_transform_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
defmodule Imager.Config.StoreTransformTest do
use ExUnit.Case, async: true
use ExUnitProperties

alias Imager.Config.StoreTransform, as: Subject

defp type(types \\ ~w(S3 Local Blackhole)),
do: one_of(Enum.map(types, &constant/1))

defp store_config(typ \\ type(), options \\ constant(%{})) do
fixed_map(%{type: typ, options: options})
end

property "returns map with stringified atoms" do
check all path <- atom(:alphanumeric),
config <- store_config()
do
assert Map.has_key?(Subject.transform(:stores, %{path => config}), Atom.to_string(path))
end
end

property "returns map with equal fields `store` and `cache`" do
check all config <- store_config() do
assert %{"foo" => result} = Subject.transform(:stores, %{foo: config})
assert %{store: data, cache: data} = result
end
end

property "returns map with different fields `store` and `cache` when both are defined" do
check all [t1, t2] <- uniq_list_of(type(), length: 2),
store <- store_config(constant(t1)),
cache <- store_config(constant(t2))
do
assert %{"foo" => result} = Subject.transform(:stores, %{foo: %{store: store, cache: cache}})
assert %{store: store_tuple, cache: cache_tuple} = result
assert store_tuple != cache_tuple
end
end

property "returns map with different fields `store` and `cache` when any is set" do
check all [t1, t2] <- uniq_list_of(type(), length: 2),
main <- store_config(constant(t1)),
other <- store_config(constant(t2)),
field <- one_of([constant(:cache), constant(:store)]),
config = Map.put(main, field, other)
do
assert %{"foo" => result} = Subject.transform(:stores, %{foo: config})
assert %{store: store_tuple, cache: cache_tuple} = result
assert store_tuple != cache_tuple
end
end

property "raises when there is unknown type" do
check all typ <- string(:alphanumeric),
typ not in ~w(S3 Local Blackhole),
config <- store_config(constant(typ))
do
assert_raise RuntimeError, fn ->
Subject.transform(:stores, %{foo: config})
end
end
end

property "raises when path contains slash" do
check all a <- string([?a..?z]),
b <- string([?a..?z]),
path = a <> "/" <> b,
config <- store_config()
do
assert_raise RuntimeError, "'#{path}' cannot contain '/'", fn ->
Subject.transform(:stores, %{path => config})
end
end
end

test "raises when uses reserved name" do
assert_raise RuntimeError, "'health' is reserved name", fn ->
Subject.transform(:stores, %{health: %{type: "Blackhole"}})
end
end

property "other values are passed through unchanged" do
check all key <- atom(:alphanumeric),
key != :stores,
value <- term()
do
assert value == Subject.transform(key, value)
end
end
end
37 changes: 37 additions & 0 deletions test/imager/config_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
defmodule Imager.ConfigTest do
use ExUnit.Case, async: true

alias Imager.Config, as: Subject

test "ignores non-existent files" do
assert :ok = Subject.init(path: "non-existent-file.toml")
end

test "raises on missing path param" do
assert_raise KeyError, fn ->
Subject.init([])
end
end

test "raises Toml.Error on invalid TOML" do
assert_raise Toml.Error, fn ->
Subject.init(path: "test/fixtures/incorrect.config.toml")
end
end

test "sets config after successful run" do
assert :ok = Subject.init(path: "test/fixtures/correct.config.toml")
assert %{"test" => _} = Application.get_env(:imager, :stores)

Application.delete_env(:imager, :stores)
end

test "merges config after successful run" do
Application.put_env(:imager, :stores, %{"bar" => nil})

assert :ok = Subject.init(path: "test/fixtures/correct.config.toml")
assert %{"test" => _, "bar" => _} = Application.get_env(:imager, :stores)

Application.delete_env(:imager, :stores)
end
end
26 changes: 26 additions & 0 deletions test/imager/stats_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
defmodule Imager.StatsTest do
use ExUnit.Case, async: true
use ExUnitProperties

property "tags contain all provided entries" do
check all tags <- list_of(string(:alphanumeric)) do
generated = Imager.Stats.tags(tags)

for tag <- tags do
assert tag in generated
end
end
end

property "tags contains default tags" do
check all tags <- list_of(string(:alphanumeric)) do
generated = Imager.Stats.tags(tags)

assert "app:imager" in generated
assert Enum.find(generated, fn
"version:" <> _ -> true
_ -> false
end)
end
end
end
Loading

0 comments on commit cee2b43

Please sign in to comment.