Add failing test reproducing #53

Add failing test reproducing #53

defmodule Credo.Check.Warning.UnusedEnumOperationTest do
  use Credo.TestHelper

  @described_check Credo.Check.Warning.UnusedEnumOperation

  test "it should NOT report expected code" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    Enum.join(parameter1) + parameter2
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report when result is piped" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    Enum.join(parameter1)
    |>  some_where

    parameter1
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when end of pipe AND return value" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    parameter1 + parameter2
    |> Enum.join(parameter1)
  end
end
  """ |> to_source_file
  |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside of pipe" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    parameter1 + parameter2
    |> Enum.join(parameter1)
    |> some_func_who_knows_what_it_does

    :ok
  end
end
  """ |> to_source_file
  |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside an assignment" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    offset = Enum.count(line)

    parameter1 + parameter2 + offset
  end
end
  """ |> to_source_file
  |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside a condition" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    if Enum.count(x1) > Enum.count(x2) do
      cond do
        Enum.count(x3) == "" -> IO.puts("1")
        Enum.count(x) == 15 -> IO.puts("2")
        Enum.at(x3, 1) == "b" -> IO.puts("2")
      end
    else
      case Enum.count(x3) do
        0 -> true
        1 -> false
        _ -> something
      end
    end
    unless Enum.count(x4) == "" do
      IO.puts "empty"
    end

    parameter1 + parameter2 + offset
  end
end
  """ |> to_source_file
      |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside a quote" do
"""
defmodule CredoSampleModule do
  defp category_body(nil) do
    quote do
      __MODULE__
      |> Module.split
      |> Enum.at(2)
    end
  end
end
  """ |> to_source_file
      |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside of assignment" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    pos =
      pos_string(issue.line_no, issue.column)

    [
      Output.issue_color(issue), "┃ ",
      Output.check_tag(check), " ", priority |> Output.priority_arrow,
      :normal, :white, " ", message,
    ]
    |> IO.ANSI.format
    |> IO.puts

    if issue.column do
      offset = Enum.count(line)
      [
          Enum.join(x, " "), :faint, Enum.join(w, ","),
      ]
      |> IO.puts
    end

    [Output.issue_color(issue), :faint, "┃ "]
    |> IO.ANSI.format
    |> IO.puts
  end

end
  """ |> to_source_file
      |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when call is buried in else block but is the last call" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      [:this_actually_might_return, Enum.join(w, ","), :ok] # THIS is not the last_call!
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when call is buried in else block and is not the last call, but the result is assigned to a variable" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    result =
      if issue.column do
        IO.puts "."
      else
        [:this_goes_nowhere, Enum.join(w, ",")]
      end

    IO.puts "8"
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when buried in :if, :when and :fn 2" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      case check do
        true -> false
        _ ->
          Enum.reduce(arr, fn(w) ->
            [:this_might_return, Enum.join(w, ",")]
          end)
      end
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when :for and :case" do
"""
defmodule CredoSampleModule do
  defp convert_parsers(parsers) do
    for parser <- parsers do
      case Atom.to_string(parser) do
        "Elixir." <> _ -> parser
        reference      -> Enum.at(reference)
      end
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when part of a function call" do
"""
defmodule CredoSampleModule do
  defp convert_parsers(parsers) do
    for parser <- parsers do
      case Atom.to_string(parser) do
        "Elixir." <> _ -> parser
        reference      -> Module.concat(Plug.Parsers, Enum.at(reference))
      end
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when :for and :case 2" do
"""
defmodule CredoSampleModule do
  defp convert_parsers(parsers) do
    for x <- Enum.flat_map(bin, &(&1.blob)), x != "", do: x
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when :for 2" do
"""
defmodule CredoSampleModule do
  defp sum(_) do
    for x <- [1..3, 5..10], sum=Enum.sum(x), do: sum
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in :after block" do

"""
  defp my_function(fun, opts) do
    try do
      :fprof.analyse(
        dest: analyse_dest,
        totals: true,
        details: Keyword.get(opts, :details, false),
        callers: Keyword.get(opts, :callers, false),
        sort: sorting
      )
    else
      :ok ->
        {_in, analysis_output} = StringIO.contents(analyse_dest)
        Enum.count(analysis_output)
    after
      StringIO.close(analyse_dest)
    end
  end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in function call" do
"""
  def my_function(url) when is_binary(url) do
    if info.userinfo do
      destructure [username, password], Enum.join(info.userinfo, ":")
    end

    Enum.reject(opts, fn {_k, v} -> is_nil(v) end)
  end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in function call 2" do
"""
  defp print_process(pid_atom, count, own) do
    IO.puts([?", Enum.join(own, "-")])
    IO.write format_item(Path.join(path, item), Enum.take(item, width))
    print_row(["s", "B", "s", ".3f", "s"], [count, "", own, ""])
  end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in list that is returned" do
"""
defp indent_line(str, indentation, with \\\\ " ") do
  [Enum.join(with, indentation), str]
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should report a violation when buried in :if, :when and :fn" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      case check do
        true -> false
        _ ->
          list =
            Enum.reduce(arr, fn(w) ->
              [:this_goes_nowhere, Enum.join(w, ",")]
            end)
      end
    end

    IO.puts "x"
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      cols |> Enum.map(coercer)
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a tuple" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      {cols |> Enum.map(coercer), 123}
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a list" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      [cols |> Enum.map(coercer), 123]
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a keyword list" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      [cols: cols |> Enum.map(coercer), number: 123]
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a map" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      %{cols: cols |> Enum.map(coercer), number: 123}
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for ++ in :if" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      {nil, cols ++ Enum.map(cols, coercer)}
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should report a violation" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    x = parameter1 + parameter2

    Enum.at(parameter1, x)

    parameter1
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when end of pipe" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    parameter1 + parameter2
    |> Enum.at(parameter1)

    parameter1
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when buried in :for" do
"""
defmodule CredoSampleModule do
  defp print_issue(w) do
    for x <- [1, 2, 3] do
      # this goes nowhere!
      Enum.join(w, ",")
      x
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when buried in :if" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      [
        :this_goes_nowhere,
        Enum.join(w, ",") # THIS is not the last_call!
      ]
      IO.puts "."
    else
      IO.puts "x"
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when buried in :else" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      Enum.count(filename)
      IO.puts "x"
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when buried in :if, :when and :fn 2" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      case check do
        true -> false
        _ ->
          Enum.reduce(arr, fn(w) ->
            [:this_goes_nowhere, Enum.join(w, ",")]
          end)
      end
    end

    IO.puts "x"
  end
end
""" |> to_source_file
    |> assert_issues(@described_check)
  end

  test "it should report a violation when call is buried in else block but is the last call" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      [:this_goes_nowhere, Enum.join(w, ",")] # THIS is not the last_call!
    end

    IO.puts
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when call is buried in else block but is the last call 2" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      [:this_goes_nowhere, Enum.join(w, ",")] # THIS is not the last_call!
      IO.puts " "
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check, fn(issue) ->
        assert "Enum.join" == issue.trigger
      end)
  end

  test "it should report several violations" do
  """
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    Enum.reject(parameter1, &is_nil/1)
    parameter1
  end
  def some_function2(parameter1, parameter2) do
   Enum.reduce(parameter1, parameter2)
   parameter1
   end
   def some_function3(parameter1, parameter2) do
     Enum.reduce(parameter1, parameter2)
     parameter1
   end
end
""" |> to_source_file
    |> assert_issues(@described_check, fn(issues) ->
        assert 3 == Enum.count(issues)
      end)
  end

  test "it should report a violation when used incorrectly, even inside a :for" do
"""
defmodule CredoSampleModule do
  defp something(bin) do
    for segment <- Enum.flat_map(segment, &(&1.blob)), segment != "" do
      Enum.map(segment, &IO.inspect/1)
      segment
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check, fn(issue) ->
        assert "Enum.map" == issue.trigger
      end)
  end

end

defmodule Credo.Check.Warning.UnusedEnumOperationTest do
  use Credo.TestHelper

  @described_check Credo.Check.Warning.UnusedEnumOperation

  test "it should NOT report expected code" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    Enum.join(parameter1) + parameter2
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report when result is piped" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    Enum.join(parameter1)
    |>  some_where

    parameter1
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when end of pipe AND return value" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    parameter1 + parameter2
    |> Enum.join(parameter1)
  end
end
  """ |> to_source_file
  |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside of pipe" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    parameter1 + parameter2
    |> Enum.join(parameter1)
    |> some_func_who_knows_what_it_does

    :ok
  end
end
  """ |> to_source_file
  |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside an assignment" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    offset = Enum.count(line)

    parameter1 + parameter2 + offset
  end
end
  """ |> to_source_file
  |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside a condition" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    if Enum.count(x1) > Enum.count(x2) do
      cond do
        Enum.count(x3) == "" -> IO.puts("1")
        Enum.count(x) == 15 -> IO.puts("2")
        Enum.at(x3, 1) == "b" -> IO.puts("2")
      end
    else
      case Enum.count(x3) do
        0 -> true
        1 -> false
        _ -> something
      end
    end
    unless Enum.count(x4) == "" do
      IO.puts "empty"
    end

    parameter1 + parameter2 + offset
  end
end
  """ |> to_source_file
      |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside a quote" do
"""
defmodule CredoSampleModule do
  defp category_body(nil) do
    quote do
      __MODULE__
      |> Module.split
      |> Enum.at(2)
    end
  end
end
  """ |> to_source_file
      |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when inside of assignment" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    pos =
      pos_string(issue.line_no, issue.column)

    [
      Output.issue_color(issue), "┃ ",
      Output.check_tag(check), " ", priority |> Output.priority_arrow,
      :normal, :white, " ", message,
    ]
    |> IO.ANSI.format
    |> IO.puts

    if issue.column do
      offset = Enum.count(line)
      [
          Enum.join(x, " "), :faint, Enum.join(w, ","),
      ]
      |> IO.puts
    end

    [Output.issue_color(issue), :faint, "┃ "]
    |> IO.ANSI.format
    |> IO.puts
  end

end
  """ |> to_source_file
      |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when call is buried in else block but is the last call" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      [:this_actually_might_return, Enum.join(w, ","), :ok] # THIS is not the last_call!
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when call is buried in else block and is not the last call, but the result is assigned to a variable" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    result =
      if issue.column do
        IO.puts "."
      else
        [:this_goes_nowhere, Enum.join(w, ",")]
      end

    IO.puts "8"
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when buried in :if, :when and :fn 2" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      case check do
        true -> false
        _ ->
          Enum.reduce(arr, fn(w) ->
            [:this_might_return, Enum.join(w, ",")]
          end)
      end
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when :for and :case" do
"""
defmodule CredoSampleModule do
  defp convert_parsers(parsers) do
    for parser <- parsers do
      case Atom.to_string(parser) do
        "Elixir." <> _ -> parser
        reference      -> Enum.at(reference)
      end
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when part of a function call" do
"""
defmodule CredoSampleModule do
  defp convert_parsers(parsers) do
    for parser <- parsers do
      case Atom.to_string(parser) do
        "Elixir." <> _ -> parser
        reference      -> Module.concat(Plug.Parsers, Enum.at(reference))
      end
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when :for and :case 2" do
"""
defmodule CredoSampleModule do
  defp convert_parsers(parsers) do
    for segment <- Enum.flat_map(bin, &(&1.blob)), segment != "", do: segment
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in :after block" do

"""
  defp my_function(fun, opts) do
    try do
      :fprof.analyse(
        dest: analyse_dest,
        totals: true,
        details: Keyword.get(opts, :details, false),
        callers: Keyword.get(opts, :callers, false),
        sort: sorting
      )
    else
      :ok ->
        {_in, analysis_output} = StringIO.contents(analyse_dest)
        Enum.count(analysis_output)
    after
      StringIO.close(analyse_dest)
    end
  end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in function call" do
"""
  def my_function(url) when is_binary(url) do
    if info.userinfo do
      destructure [username, password], Enum.join(info.userinfo, ":")
    end

    Enum.reject(opts, fn {_k, v} -> is_nil(v) end)
  end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in function call 2" do
"""
  defp print_process(pid_atom, count, own) do
    IO.puts([?", Enum.join(own, "-")])
    IO.write format_item(Path.join(path, item), Enum.take(item, width))
    print_row(["s", "B", "s", ".3f", "s"], [count, "", own, ""])
  end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation when in list that is returned" do
"""
defp indent_line(str, indentation, with \\\\ " ") do
  [Enum.join(with, indentation), str]
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should report a violation when buried in :if, :when and :fn" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      case check do
        true -> false
        _ ->
          list =
            Enum.reduce(arr, fn(w) ->
              [:this_goes_nowhere, Enum.join(w, ",")]
            end)
      end
    end

    IO.puts "x"
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      cols |> Enum.map(coercer)
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a tuple" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      {cols |> Enum.map(coercer), 123}
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a list" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      [cols |> Enum.map(coercer), 123]
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a keyword list" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      [cols: cols |> Enum.map(coercer), number: 123]
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for |> in :if when part of a map" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      %{cols: cols |> Enum.map(coercer), number: 123}
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

  test "it should NOT report a violation for ++ in :if" do
"""
defmodule CredoSampleModule do
  def sort_column(col, query) do
    cols = result_columns(query)
    if hd(cols) do
      coercer = fn({name, type}) -> coerce(type, col[Atom.to_string(name)]) end
      {nil, cols ++ Enum.map(cols, coercer)}
    else
      [nil]
    end
  end
end
""" |> to_source_file
    |> refute_issues(@described_check)
  end

 ##############################################################################
 ##############################################################################

  test "it should report a violation" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    x = parameter1 + parameter2

    Enum.at(parameter1, x)

    parameter1
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when end of pipe" do
"""
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    parameter1 + parameter2
    |> Enum.at(parameter1)

    parameter1
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when buried in :if" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      [
        :this_goes_nowhere,
        Enum.join(w, ",") # THIS is not the last_call!
      ]
      IO.puts "."
    else
      IO.puts "x"
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when buried in :else" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      Enum.count(filename)
      IO.puts "x"
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when buried in :if, :when and :fn 2" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      case check do
        true -> false
        _ ->
          Enum.reduce(arr, fn(w) ->
            [:this_goes_nowhere, Enum.join(w, ",")]
          end)
      end
    end

    IO.puts "x"
  end
end
""" |> to_source_file
    |> assert_issues(@described_check)
  end

  test "it should report a violation when call is buried in else block but is the last call" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      [:this_goes_nowhere, Enum.join(w, ",")] # THIS is not the last_call!
    end

    IO.puts
  end
end
""" |> to_source_file
    |> assert_issue(@described_check)
  end

  test "it should report a violation when call is buried in else block but is the last call 2" do
"""
defmodule CredoSampleModule do
  defp print_issue(%Issue{check: check, message: message, filename: filename, priority: priority} = issue, source_file) do
    if issue.column do
      IO.puts "."
    else
      [:this_goes_nowhere, Enum.join(w, ",")] # THIS is not the last_call!
      IO.puts " "
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check, fn(issue) ->
        assert "Enum.join" == issue.trigger
      end)
  end

  test "it should report several violations" do
  """
defmodule CredoSampleModule do
  def some_function(parameter1, parameter2) do
    Enum.reject(parameter1, &is_nil/1)
    parameter1
  end
  def some_function2(parameter1, parameter2) do
   Enum.reduce(parameter1, parameter2)
   parameter1
   end
   def some_function3(parameter1, parameter2) do
     Enum.reduce(parameter1, parameter2)
     parameter1
   end
end
""" |> to_source_file
    |> assert_issues(@described_check, fn(issues) ->
        assert 3 == Enum.count(issues)
      end)
  end

  test "it should report a violation when used incorrectly, even inside a :for" do
"""
defmodule CredoSampleModule do
  defp something(bin) do
    for segment <- Enum.flat_map(segment, &(&1.blob)), segment != "" do
      Enum.map(segment, &IO.inspect/1)
      segment
    end
  end
end
""" |> to_source_file
    |> assert_issue(@described_check, fn(issue) ->
        assert "Enum.map" == issue.trigger
      end)
  end

end


going to branch and fix bot state.. again

ExUnit.start
defmodule Farmbot.BotStateTest do
  alias Farmbot.BotState, as: Bot
  use ExUnit.Case, async: true
  require IEx

  test "gets the current status" do
    # We drop the authorization key/value pair because the state handle call drops it.
    {:ok, with_auth_status} = Farmbot.BotState.init(
          %{target: "test", compat_version: 0, env: :test, version: "2.1.4"})
    status = with_auth_status |> Map.drop([:authorization])
    {:reply, current_status, current_status} = Farmbot.BotState.handle_call(:state, self(), status)
    # We havent changed anything so the status should be in its initial state.
    assert(status == current_status)
  end

  test "sets a pin value on and then off again." do
    Farmbot.BotState.set_pin_mode(13, 0)
    Farmbot.BotState.set_pin_value(13, 1)

    a = Farmbot.BotState.get_pin(13)
    assert(a == %{mode: 0, value: 1})

    Farmbot.BotState.set_pin_value(13, 0)
    b = Farmbot.BotState.get_pin(13)
    assert(b == %{mode: 0, value: 0})
  end

  test "checks and sets bot position" do
    assert(Farmbot.BotState.get_current_pos == [0,0,0])
    Farmbot.BotState.set_pos(1,2,3)
    assert(Farmbot.BotState.get_current_pos == [1,2,3])
  end

  test "updates a configuration key that DOES exist." do
    # keys that exist should return true
    update_bool = Farmbot.BotState.update_config("os_auto_update", false)
    q = Farmbot.BotState.get_config(:os_auto_update)
    assert(update_bool == true)
    assert(q == false)
  end

  test "updates a configuration key that DOES NOT exist." do
    # keys that don't exist should return false
    update_bool = Farmbot.BotState.update_config("check_email_time", 15000)
    q = Farmbot.BotState.get_config(:check_email_time)
    assert(q == nil)
    assert(update_bool == false)
  end

  test "updates a configuration key that DOES exist but the type is bad." do
    # Before we try to update it.
    old = Farmbot.BotState.get_config(:os_auto_update)
    # should return false
    update_bool = Farmbot.BotState.update_config("os_auto_update", "silly_string")
    new = Farmbot.BotState.get_config(:os_auto_update)
    assert(old == new)
    assert(update_bool == false)
  end

  test "locks the bot" do
    e_stop = "e_stop"
    Farmbot.BotState.add_lock(e_stop)
    state = Bot.get_status
    locks = state.locks

    assert(locks |> is_list)
    assert(locks = [%{readon: e_stop}])

    Bot.remove_lock(e_stop)
    new_state = Bot.get_status
    new_state = new_state.locks

    assert(locks |> is_list)
    assert(locks = [])
  end
end

ExUnit.start
defmodule Farmbot.BotStateTest do
  use ExUnit.Case, async: true

  test "gets the current status" do
    # We drop the authorization key/value pair because the state handle call drops it.
    {:ok, with_auth_status} = Farmbot.BotState.init(
          %{target: "test", compat_version: 0, env: :test, version: "2.1.4"})
    status = with_auth_status |> Map.drop([:authorization])
    {:reply, current_status, current_status} = Farmbot.BotState.handle_call(:state, self(), status)
    # We havent changed anything so the status should be in its initial state.
    assert(status == current_status)
  end

  test "sets a pin value on and then off again." do
    Farmbot.BotState.set_pin_mode(13, 0)
    Farmbot.BotState.set_pin_value(13, 1)

    a = Farmbot.BotState.get_pin(13)
    assert(a == %{mode: 0, value: 1})

    Farmbot.BotState.set_pin_value(13, 0)
    b = Farmbot.BotState.get_pin(13)
    assert(b == %{mode: 0, value: 0})
  end

  test "checks and sets bot position" do
    assert(Farmbot.BotState.get_current_pos == [0,0,0])
    Farmbot.BotState.set_pos(1,2,3)
    assert(Farmbot.BotState.get_current_pos == [1,2,3])
  end

  test "updates a configuration key that DOES exist." do
    # keys that exist should return true
    update_bool = Farmbot.BotState.update_config("os_auto_update", false)
    q = Farmbot.BotState.get_config(:os_auto_update)
    assert(update_bool == true)
    assert(q == false)
  end

  test "updates a configuration key that DOES NOT exist." do
    # keys that don't exist should return false
    update_bool = Farmbot.BotState.update_config("check_email_time", 15000)
    q = Farmbot.BotState.get_config(:check_email_time)
    assert(q == nil)
    assert(update_bool == false)
  end

  test "updates a configuration key that DOES exist but the type is bad." do
    # Before we try to update it.
    old = Farmbot.BotState.get_config(:os_auto_update)
    # should return false
    update_bool = Farmbot.BotState.update_config("os_auto_update", "silly_string")
    new = Farmbot.BotState.get_config(:os_auto_update)
    assert(old == new)
    assert(update_bool == false)
  end
end


update infinity

update infinity

IO.puts """
        `
       ;:
      `+;
      ++',
     .+++,
     :'++'.
    .+'++';       Welcome to elixir-land
    ,+'+''':
   `:+'+';';;     http://elixir-lang.org
   ,:++';;;;'.    http://www.phoenixframework.org/
   ::'+';;;;''    https://hex.pm/
   :;;++;;';''    http://www.elixirschool.com/
   .;;'''';;;:    https://github.com/christopheradams/elixir_style_guide
    ,:::'';,,.
    ,,,,,:::,
     ,,,,,,,
      ``.`
"""
IEx.configure(
  alive_prompt: "%prefix(%node):%counter>",
  default_prompt: "%prefix:%counter>",
  inspect: [pretty: true, char_lists: :as_lists, limit: :infinity],
  history_size: -1
)

import_file_if_available(".secret.exs")

rs = fn ->
  Process.exit(self(), :normal)
end

IO.puts """
        `
       ;:
      `+;
      ++',
     .+++,
     :'++'.
    .+'++';       Welcome to elixir-land
    ,+'+''':
   `:+'+';';;     http://elixir-lang.org
   ,:++';;;;'.    http://www.phoenixframework.org/
   ::'+';;;;''    https://hex.pm/
   :;;++;;';''    http://www.elixirschool.com/
   .;;'''';;;:    https://github.com/christopheradams/elixir_style_guide
    ,:::'';,,.
    ,,,,,:::,
     ,,,,,,,
      ``.`
"""
IEx.configure(
  alive_prompt: "%prefix(%node):%counter>",
  default_prompt: "%prefix:%counter>",
  inspect: [pretty: true, char_lists: :as_lists],
  history_size: -1
)

import_file_if_available(".secret.exs")

rs = fn ->
  Process.exit(self(), :normal)
end


WIP New plugin permissions

WIP New plugin permissions

require Logger

defmodule Serverboards.Plugin.RPC do
  alias MOM.RPC
  alias Serverboards.Plugin

  TODO get the plugin definition (if id is a plugin) and the user_perms to be checked too

  def has_perm_for_plugin(context, plugin) do
    perms = RPC.Context.get(context, :user).perms
    cond do
      plugin == :any -> Enum.any?(perms, &(String.starts_with?(&1, "plugin[") or &1 == "plugin"))
      "plugin" in perms -> true
      "plugin[#{plugin}]" in perms -> true
      true -> false
    end
  end
  def check_perm_for_plugin_data(context) do
    user = RPC.Context.get(context, :user)
    context_plugin = RPC.Context.get(context, :plugin)
    #Logger.debug("Plugin dataA #{inspect context_plugin}")
    case context_plugin do
      nil -> # not called from plugin, error
        false
      %{ plugin_id: plugin } -> # called from plugin, allow only to myself
        {plugin, user}
    end
  end
  def check_perm_for_plugin_data(context, plugin) do
    user = RPC.Context.get(context, :user)
    context_plugin = RPC.Context.get(context, :plugin)
    #Logger.debug("Plugin dataB #{inspect context_plugin}")
    case context_plugin do
      nil -> # not called from plugin, check permissions
        perms = user.perms
        cond do
          "plugin.data" in perms -> {plugin, user}
          "plugin.data[#{plugin}]" in perms -> {plugin, user}
          true -> false
        end
      %{ plugin_id: ^plugin } -> # called from plugin, allow only to myself
        {plugin, user}
      %{ plugin_id: plugin_id } -> # error on others
        Logger.error("Plugin #{inspect plugin_id} does not have access to plugin data from #{inspect plugin}")
        false
    end
  end

  def start_link do
    {:ok, method_caller} = RPC.MethodCaller.start_link name: Serverboards.Plugin.RPC
    Serverboards.Utils.Decorators.permission_method_caller method_caller

    RPC.MethodCaller.add_method method_caller, "plugin.start", fn [plugin_component_id], context ->
      if has_perm_for_plugin(context, plugin_component_id) do
        user = RPC.Context.get(context, :user)
        Plugin.Runner.start plugin_component_id, user.email
      else
        {:error, :unknown_method}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.stop", fn [uuid], context ->
      if has_perm_for_plugin(context, :any) do
        Plugin.Runner.stop uuid
      else
        {:error, :unknown_method}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.kill", fn [uuid_or_id], context ->
      if has_perm_for_plugin(context, :any) do
        user = RPC.Context.get(context, :user)
        uuid = case UUID.info(uuid_or_id) do
          {:ok, _info} -> uuid_or_id
          _ ->
            case Plugin.Runner.get_by_component_id(uuid_or_id) do
              {:ok, uuid} -> uuid
              _ -> nil
            end
        end
        if uuid do
          Logger.warn("Plugin #{inspect uuid_or_id} killed by #{inspect user.email}", plugin_uuid: uuid, user: user.email)
          Plugin.Runner.kill uuid
        else
          {:error, :not_running}
        end
      else
        {:error, :unknown_method}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.call", fn
      [id, method, params] ->
        call_plugin(context, id, method, params)
      [id, method] ->
        call_plugin(context, id, method, [])
    end, [context: true]

    RPC.MethodCaller.add_method method_caller, "plugin.is_running", fn
      [id], context ->
        if has_perm_for_plugin(context, :any) do
          Plugin.Runner.status(id) == :running
        else
          {:error, :unknown_method}
        end
    end, [required_perm: "plugin", context: true]

    RPC.MethodCaller.add_method method_caller, "plugin.ps", fn
      [] -> Plugin.Runner.ps()
    end, [required_perm: "plugin"]

    RPC.MethodCaller.add_method method_caller, "plugin.catalog", fn
      [] ->
        Serverboards.Plugin.Registry.list
      %{} ->
        Serverboards.Plugin.Registry.list
    end, [required_perm: "plugin"]
    RPC.MethodCaller.add_method method_caller, "plugin.install", fn
      [url] ->
        Serverboards.Plugin.Installer.install(url)
    end, [required_perm: "plugin.install"]

    RPC.MethodCaller.add_method(method_caller, "plugin.data.update", fn
      [ key, value ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, user} ->
            Plugin.Data.data_set(plugin, key, value, user)
          _ ->
            {:error, :not_allowed}
        end
      [ plugin, key, value ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, user} ->
            Plugin.Data.data_set(plugin, key, value, user)
          _ ->
            {:error, :not_allowed}
        end
      end,
      context: true)

    RPC.MethodCaller.add_method(method_caller, "plugin.data.get", fn
      [ key ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_get(plugin, key)}
          _ ->
            {:error, :not_allowed}
        end
      [ plugin, key ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_get(plugin, key)}
          _ ->
            {:error, :not_allowed}
        end
      end,
      context: true)

    RPC.MethodCaller.add_method method_caller, "plugin.data.list", fn
      [ plugin, keyprefix ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_keys(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
      [ keyprefix ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_keys(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
    end, context: true
    RPC.MethodCaller.add_method method_caller, "plugin.data.items", fn
      [ plugin, keyprefix ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_items(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
      [ keyprefix ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_items(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.data.delete",
        fn [ plugin, key ], context ->
      user = RPC.Context.get context, :user
      perms = user.perms
      can_data = (
        ("plugin.data" in perms) or
        ("plugin.data[#{plugin}]" in perms)
        )
      if can_data do
        {:ok, Plugin.Data.data_remove(plugin, key, user)}
      else
        Logger.debug("Perms #{inspect perms}, not plugin.data, nor plugin.data[#{plugin}]")
        {:error, :not_allowed}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.component.catalog", fn
      [] ->
        []
          |> Serverboards.Plugin.Registry.filter_component
          |> Serverboards.Utils.clean_struct
      [ filter ] ->
        Serverboards.Utils.keys_to_atoms_from_list(filter, ~w"type id trait traits")
          |> Serverboards.Plugin.Registry.filter_component
          |> Serverboards.Utils.clean_struct
      %{} = filter ->
        Serverboards.Utils.keys_to_atoms_from_list(filter, ~w"type id trait traits")
          |> Serverboards.Plugin.Registry.filter_component
          |> Serverboards.Utils.clean_struct
    end

    {:ok, method_caller}
  end

  def call_plugin(context, id, method, params) do
    if has_perm_for_plugin(context, id) do
      Plugin.Runner.call id, method, params
    else
      {:error, :unknown_method}
    end
  end
end

require Logger

defmodule Serverboards.Plugin.RPC do
  alias MOM.RPC
  alias Serverboards.Plugin

  def has_perm_for_plugin(context, plugin) do
    perms = RPC.Context.get(context, :user).perms
    cond do
      plugin == :any -> Enum.any?(perms, &(String.starts_with?(&1, "plugin[") or &1 == "plugin"))
      "plugin" in perms -> true
      "plugin[#{plugin}]" in perms -> true
      true -> false
    end
  end
  def check_perm_for_plugin_data(context) do
    user = RPC.Context.get(context, :user)
    context_plugin = RPC.Context.get(context, :plugin)
    #Logger.debug("Plugin dataA #{inspect context_plugin}")
    case context_plugin do
      nil -> # not called from plugin, error
        false
      %{ plugin_id: plugin } -> # called from plugin, allow only to myself
        {plugin, user}
    end
  end
  def check_perm_for_plugin_data(context, plugin) do
    user = RPC.Context.get(context, :user)
    context_plugin = RPC.Context.get(context, :plugin)
    #Logger.debug("Plugin dataB #{inspect context_plugin}")
    case context_plugin do
      nil -> # not called from plugin, check permissions
        perms = user.perms
        cond do
          "plugin.data" in perms -> {plugin, user}
          "plugin.data[#{plugin}]" in perms -> {plugin, user}
          true -> false
        end
      %{ plugin_id: ^plugin } -> # called from plugin, allow only to myself
        {plugin, user}
      %{ plugin_id: plugin_id } -> # error on others
        Logger.error("Plugin #{inspect plugin_id} does not have access to plugin data from #{inspect plugin}")
        false
    end
  end

  def start_link do
    {:ok, method_caller} = RPC.MethodCaller.start_link name: Serverboards.Plugin.RPC
    Serverboards.Utils.Decorators.permission_method_caller method_caller

    RPC.MethodCaller.add_method method_caller, "plugin.start", fn [plugin_component_id], context ->
      if has_perm_for_plugin(context, plugin_component_id) do
        user = RPC.Context.get(context, :user)
        Plugin.Runner.start plugin_component_id, user.email
      else
        {:error, :unknown_method}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.stop", fn [uuid], context ->
      if has_perm_for_plugin(context, :any) do
        Plugin.Runner.stop uuid
      else
        {:error, :unknown_method}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.kill", fn [uuid_or_id], context ->
      if has_perm_for_plugin(context, :any) do
        user = RPC.Context.get(context, :user)
        uuid = case UUID.info(uuid_or_id) do
          {:ok, _info} -> uuid_or_id
          _ ->
            case Plugin.Runner.get_by_component_id(uuid_or_id) do
              {:ok, uuid} -> uuid
              _ -> nil
            end
        end
        if uuid do
          Logger.warn("Plugin #{inspect uuid_or_id} killed by #{inspect user.email}", plugin_uuid: uuid, user: user.email)
          Plugin.Runner.kill uuid
        else
          {:error, :not_running}
        end
      else
        {:error, :unknown_method}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.call", fn
      [id, method, params] ->
        Plugin.Runner.call id, method, params
      [id, method] ->
        Plugin.Runner.call id, method, []
    end, [required_perm: "plugin"]

    RPC.MethodCaller.add_method method_caller, "plugin.is_running", fn
      [id], context ->
        if has_perm_for_plugin(context, :any) do
          Plugin.Runner.status(id) == :running
        else
          {:error, :unknown_method}
        end
    end, [required_perm: "plugin", context: true]

    RPC.MethodCaller.add_method method_caller, "plugin.ps", fn
      [] -> Plugin.Runner.ps()
    end, [required_perm: "plugin"]

    RPC.MethodCaller.add_method method_caller, "plugin.catalog", fn
      [] ->
        Serverboards.Plugin.Registry.list
      %{} ->
        Serverboards.Plugin.Registry.list
    end, [required_perm: "plugin"]
    RPC.MethodCaller.add_method method_caller, "plugin.install", fn
      [url] ->
        Serverboards.Plugin.Installer.install(url)
    end, [required_perm: "plugin.install"]

    RPC.MethodCaller.add_method(method_caller, "plugin.data.update", fn
      [ key, value ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, user} ->
            Plugin.Data.data_set(plugin, key, value, user)
          _ ->
            {:error, :not_allowed}
        end
      [ plugin, key, value ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, user} ->
            Plugin.Data.data_set(plugin, key, value, user)
          _ ->
            {:error, :not_allowed}
        end
      end,
      context: true)

    RPC.MethodCaller.add_method(method_caller, "plugin.data.get", fn
      [ key ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_get(plugin, key)}
          _ ->
            {:error, :not_allowed}
        end
      [ plugin, key ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_get(plugin, key)}
          _ ->
            {:error, :not_allowed}
        end
      end,
      context: true)

    RPC.MethodCaller.add_method method_caller, "plugin.data.list", fn
      [ plugin, keyprefix ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_keys(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
      [ keyprefix ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_keys(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
    end, context: true
    RPC.MethodCaller.add_method method_caller, "plugin.data.items", fn
      [ plugin, keyprefix ], context ->
        case check_perm_for_plugin_data( context, plugin ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_items(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
      [ keyprefix ], context ->
        case check_perm_for_plugin_data( context ) do
          {plugin, _user} ->
            {:ok, Plugin.Data.data_items(plugin, keyprefix)}
          _ ->
            {:error, :not_allowed}
        end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.data.delete",
        fn [ plugin, key ], context ->
      user = RPC.Context.get context, :user
      perms = user.perms
      can_data = (
        ("plugin.data" in perms) or
        ("plugin.data[#{plugin}]" in perms)
        )
      if can_data do
        {:ok, Plugin.Data.data_remove(plugin, key, user)}
      else
        Logger.debug("Perms #{inspect perms}, not plugin.data, nor plugin.data[#{plugin}]")
        {:error, :not_allowed}
      end
    end, context: true

    RPC.MethodCaller.add_method method_caller, "plugin.component.catalog", fn
      [] ->
        []
          |> Serverboards.Plugin.Registry.filter_component
          |> Serverboards.Utils.clean_struct
      [ filter ] ->
        Serverboards.Utils.keys_to_atoms_from_list(filter, ~w"type id trait traits")
          |> Serverboards.Plugin.Registry.filter_component
          |> Serverboards.Utils.clean_struct
      %{} = filter ->
        Serverboards.Utils.keys_to_atoms_from_list(filter, ~w"type id trait traits")
          |> Serverboards.Plugin.Registry.filter_component
          |> Serverboards.Utils.clean_struct
    end

    {:ok, method_caller}
  end
end


rename game mode functions

rename game mode functions

changed them so they match the previous naming of 'singleplayer' | 'multiplayer'

defmodule BattleSnake.WinConditions do
  require BattleSnake.GameForm

  @spec game_mode(binary) :: (BattleSnake.World.t -> boolean)
  def game_mode(game_mode) do
    case game_mode do
      BattleSnake.GameForm.singleplayer() ->
        &singleplayer/1
      BattleSnake.GameForm.multiplayer() ->
        &multiplayer/1
    end
  end

  def singleplayer(world) do
    length(world.snakes) <= 0
  end

  def multiplayer(world) do
    length(world.snakes) <= 1
  end
end

defmodule BattleSnake.WinConditions do
  require BattleSnake.GameForm

  @spec game_mode(binary) :: (BattleSnake.World.t -> boolean)
  def game_mode(game_mode) do
    case game_mode do
      BattleSnake.GameForm.singleplayer() ->
        &single_player/1
      BattleSnake.GameForm.multiplayer() ->
        &death_match/1
    end
  end

  def single_player(world) do
    length(world.snakes) <= 0
  end

  def death_match(world) do
    length(world.snakes) <= 1
  end
end


`mix format` according to Elixir 1.6.6.

`mix format` according to Elixir 1.6.6.

defmodule Floki.TraversalTest do
  use ExUnit.Case, async: true

  describe "traverse_and_update/2" do
    test "with string returns string" do
      assert Floki.traverse_and_update("hello", fn tag -> tag end) == "hello"
    end

    test "with div and identity function returns div" do
      html = {"div", [], ["hello"]}

      assert Floki.traverse_and_update(html, fn tag -> tag end) == html
    end

    test "with div and div->p function returns p" do
      html = {"div", [], ["hello"]}

      assert Floki.traverse_and_update(html, fn {"div", attrs, children} ->
               {"p", attrs, children}
             end) == {"p", [], ["hello"]}
    end

    test "with style attribute and style->src function returns src attribute" do
      html = {"div", [{"style", "display: flex"}], ["hello"]}

      assert Floki.traverse_and_update(html, fn {elem, _attrs, children} ->
               {elem, [{"src", "http://example.test"}], children}
             end) == {"div", [{"src", "http://example.test"}], ["hello"]}
    end

    test "with text child and child replacer function returns tag with new child" do
      html = {"div", [], ["hello"]}

      assert Floki.traverse_and_update(html, fn {elem, attrs, _children} ->
               {elem, attrs, ["world"]}
             end) == {"div", [], ["world"]}
    end

    test "with div->span and span deleter function returns div without span" do
      html = {"div", [], [{"span", [], ["hello"]}]}

      assert Floki.traverse_and_update(html, fn
               {"span", _attrs, _children} -> nil
               tag -> tag
             end) == {"div", [], []}
    end

    test "with div,p and identity function returns div,p" do
      html = [{"div", [], ["hello"]}, {"p", [], ["world"]}]

      assert Floki.traverse_and_update(html, fn tag -> tag end) == html
    end
  end
end


Prepare for Heroku

Prepare for Heroku

use Mix.Config

config :ueberauth_example, UeberauthExample.Endpoint,
  http: [port: {:system, "PORT"}],
  url: [scheme: "https", host: "ueberauth-example.herokuapp.com", port: 443],
  force_ssl: [rewrite_on: [:x_forwarded_proto]],
  cache_static_manifest: "priv/static/manifest.json",
  secret_key_base: System.get_env("SECRET_KEY_BASE")

# Do not print debug messages in production
config :logger, level: :info

# Configure your database
config :ueberauth_example, UeberauthExample.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: System.get_env("DATABASE_URL"),
  pool_size: 20

use Mix.Config

config :ueberauth_example, UeberauthExample.Endpoint,
  http: [port: {:system, "PORT"}],
  url: [host: "example.com", port: 80],
  cache_static_manifest: "priv/static/manifest.json"

# Do not print debug messages in production
config :logger, level: :info

# Configure your database
config :ueberauth_example, UeberauthExample.Repo,
  adapter: Ecto.Adapters.Postgres,
  url: System.get_env("DATABASE_URL"),
  pool_size: 20


let nginx compress instead of cowboy

let nginx compress instead of cowboy

use Mix.Config

config :pan, PanWeb.Endpoint,
  http: [port: 8888, compress: false],
  url: [scheme: "https", host: "panoptikum.io", port: 443],
  cache_static_manifest: "priv/static/cache_manifest.json",
  server: true,
  root: ".",
  check_origin: ["https://panoptikum.io", "https://ansible.local"],
  version: Mix.Project.config()[:version]

config :logger,
  backends: [:console, {Logger.Backends.ExceptionNotification, :exeception_notification}]

config :logger, level: :info
config :phoenix, :serve_endpoints, true

config :timelier,
  crontab: [
    {{42, :any, :any, :any, :any}, {PanWeb.Podcast, :import_stale_podcasts, []}},
    {{[0, 10, 20, 30, 40, 50], :any, :any, :any, :any}, {PanWeb.Image, :cache_missing, []}},
    {{[8, 18, 28, 38, 48, 58], :any, :any, :any, :any}, {Pan.Search, :push_missing, []}},
    {{0, 6, :any, :any, :any}, {PanWeb.User, :pro_expiration, []}}
  ]

config :pan, :environment, "prod"

import_config "prod.secret.exs"

use Mix.Config

config :pan, PanWeb.Endpoint,
  http: [port: 8888, compress: true],
  url: [scheme: "https", host: "panoptikum.io", port: 443],
  cache_static_manifest: "priv/static/cache_manifest.json",
  server: true,
  root: ".",
  check_origin: ["https://panoptikum.io", "https://ansible.local"],
  version: Mix.Project.config()[:version]

config :logger,
  backends: [:console, {Logger.Backends.ExceptionNotification, :exeception_notification}]

config :logger, level: :info
config :phoenix, :serve_endpoints, true

config :timelier,
  crontab: [
    {{42, :any, :any, :any, :any}, {PanWeb.Podcast, :import_stale_podcasts, []}},
    {{[0, 10, 20, 30, 40, 50], :any, :any, :any, :any}, {PanWeb.Image, :cache_missing, []}},
    {{[8, 18, 28, 38, 48, 58], :any, :any, :any, :any}, {Pan.Search, :push_missing, []}},
    {{0, 6, :any, :any, :any}, {PanWeb.User, :pro_expiration, []}}
  ]

config :pan, :environment, "prod"

import_config "prod.secret.exs"