Skip to content

Commit

Permalink
Merge pull request #12 from appunite/write-tests
Browse files Browse the repository at this point in the history
Write test suite
  • Loading branch information
hauleth committed Oct 3, 2018
2 parents 8d2fe5a + 1ef7b15 commit 2780bd6
Show file tree
Hide file tree
Showing 18 changed files with 367 additions and 24 deletions.
2 changes: 1 addition & 1 deletion .credo.exs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
# You can customize the priority of any check
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage, priority: :low},
{Credo.Check.Design.AliasUsage, priority: :low, if_called_more_often_than: 2},
# For some checks, you can also set other parameters
#
# If you don't want the `setup` and `test` macro calls in ExUnit tests
Expand Down
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"{config,lib,test}/*.{ex,exs}",
"{config,lib,test}/**/*.{ex,exs}"
],
locals_without_parens: [check: 1, gen: 1],
line_length: 79,
import_deps: [:phoenix]
]
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
21 changes: 18 additions & 3 deletions lib/imager/config/port_normalization.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,24 @@ 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_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 ->
reraise "Expected port to be positive integer, got: #{inspect(value)}",
__STACKTRACE__
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(:port, value) when is_integer(value), do: 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 @@ -26,26 +27,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 @@ -56,12 +55,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
89 changes: 89 additions & 0 deletions test/imager/config/store_transform_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
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
27 changes: 27 additions & 0 deletions test/imager/stats_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
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 2780bd6

Please sign in to comment.