Fixed SMPPEX.ESME.Sync.wait_for_pdus type

defmodule SMPPEX.ESME.Sync do

  use SMPPEX.ESME

  alias SMPPEX.ESME
  alias SMPPEX.Pdu

  require Logger

  @default_timeout 5000

  # Public interface

  def start_link(host, port, opts \\ []) do
    ESME.start_link(host, port, {__MODULE__, %{from: nil, pdu: nil, additional_pdus: [], state: :free}}, opts)
  end

  @spec request(esme :: pid, pdu :: Pdu.t, timeout :: non_neg_integer) :: {:ok, resp :: Pdu.t} | :timeout | :stop | {:error, reason :: term}

  def request(esme, pdu, timeout \\ @default_timeout) do
    try do
      ESME.call(esme, {:request, pdu}, timeout)
    catch
      :exit, {:timeout, _} -> :timeout
    end
  end

  @type awaited :: {:pdu, Pdu.t} | {:resp, Pdu.t} | {:timeout, Pdu.t} | {:error, Pdu.t, reason :: any}

  @spec wait_for_pdus(esme :: pid, timeout :: non_neg_integer) :: [awaited] | :timeout | :stop

  def wait_for_pdus(esme, timeout \\ @default_timeout) do
    try do
      ESME.call(esme, :wait_for_pdus, timeout)
    catch
      :exit, {:timeout, _} -> :timeout
    end
  end

  def pdus(esme, timeout \\ @default_timeout) do
    ESME.call(esme, :pdus, timeout)
  end

  def stop(esme) do
    ESME.stop(esme)
  end

  # ESME callbacks

  def handle_call({:request, pdu}, from, st) do
    ESME.send_pdu(self, pdu)
    new_st = %{ st | from: from, pdu: pdu, state: :wait_for_resp }
    {:noreply, new_st}
  end

  def handle_call(:pdus, _from, st) do
    do_get_pdus(st)
  end

  def handle_call(:wait_for_pdus, from, st) do
    case st.additional_pdus do
      [_ | _] -> do_get_pdus(st)
      [] ->
        new_st = %{ st | from: from, state: :wait_for_pdus }
        {:noreply, new_st}
    end
  end

  def handle_resp(pdu, original_pdu, st) do
    case Pdu.same?(original_pdu, st.pdu) and st.state == :wait_for_resp do
      true ->
        GenServer.reply(st.from, {:ok, pdu})
        do_set_free(st)
      false ->
        do_push_to_waiting({:resp, pdu, original_pdu}, st)
    end
  end

  def handle_resp_timeout(pdu, st) do
    case Pdu.same?(pdu, st.pdu) and st.state == :wait_for_resp do
      true ->
        GenServer.reply(st.from, :timeout)
        do_set_free(st)
      false ->
        do_push_to_waiting({:timeout, pdu}, st)
    end
  end

  def handle_pdu(pdu, st) do
    do_push_to_waiting({:pdu, pdu}, st)
  end

  def handle_stop(st) do
    case st.from do
      nil -> :nop
      from -> GenServer.reply(from, :stop)
    end
  end

  def handle_send_pdu_result(pdu, result, st) do
    case result do
      :ok -> st
      {:error, error} ->
        case Pdu.same?(pdu, st.pdu) and st.state == :wait_for_resp do
          true ->
            GenServer.reply(st.from, {:error, error})
            do_set_free(st)
          false ->
            do_push_to_waiting({:error, pdu, error}, st)
        end
    end
  end

  def do_push_to_waiting(pdu_info, st) do
    pdus = [pdu_info | st.additional_pdus]
    case st.state == :wait_for_pdus do
      true ->
        GenServer.reply(st.from, pdus)
        %{ do_set_free(st) | additional_pdus: [] }
      false ->
        %{ st | additional_pdus: pdus }
    end
  end

  defp do_set_free(st), do: %{ st | from: nil, pdu: nil, state: :free }

  defp do_get_pdus(st) do
    pdus = st.additional_pdus
    new_st = %{ st | additional_pdus: [] }
    {:reply, pdus, new_st}
  end

end

defmodule SMPPEX.ESME.Sync do

  use SMPPEX.ESME

  alias SMPPEX.ESME
  alias SMPPEX.Pdu

  require Logger

  @default_timeout 5000

  # Public interface

  def start_link(host, port, opts \\ []) do
    ESME.start_link(host, port, {__MODULE__, %{from: nil, pdu: nil, additional_pdus: [], state: :free}}, opts)
  end

  @spec request(esme :: pid, pdu :: Pdu.t, timeout :: non_neg_integer) :: {:ok, resp :: Pdu.t} | :timeout | :stop | {:error, reason :: term}

  def request(esme, pdu, timeout \\ @default_timeout) do
    try do
      ESME.call(esme, {:request, pdu}, timeout)
    catch
      :exit, {:timeout, _} -> :timeout
    end
  end

  @spec wait_for_pdus(esme :: pid, timeout :: non_neg_integer) :: [Pdu.t] | :timeout | :stop

  def wait_for_pdus(esme, timeout \\ @default_timeout) do
    try do
      ESME.call(esme, :wait_for_pdus, timeout)
    catch
      :exit, {:timeout, _} -> :timeout
    end
  end

  def pdus(esme, timeout \\ @default_timeout) do
    ESME.call(esme, :pdus, timeout)
  end

  def stop(esme) do
    ESME.stop(esme)
  end

  # ESME callbacks

  def handle_call({:request, pdu}, from, st) do
    ESME.send_pdu(self, pdu)
    new_st = %{ st | from: from, pdu: pdu, state: :wait_for_resp }
    {:noreply, new_st}
  end

  def handle_call(:pdus, _from, st) do
    do_get_pdus(st)
  end

  def handle_call(:wait_for_pdus, from, st) do
    case st.additional_pdus do
      [_ | _] -> do_get_pdus(st)
      [] ->
        new_st = %{ st | from: from, state: :wait_for_pdus }
        {:noreply, new_st}
    end
  end

  def handle_resp(pdu, original_pdu, st) do
    case Pdu.same?(original_pdu, st.pdu) and st.state == :wait_for_resp do
      true ->
        GenServer.reply(st.from, {:ok, pdu})
        do_set_free(st)
      false ->
        do_push_to_waiting({:resp, pdu, original_pdu}, st)
    end
  end

  def handle_resp_timeout(pdu, st) do
    case Pdu.same?(pdu, st.pdu) and st.state == :wait_for_resp do
      true ->
        GenServer.reply(st.from, :timeout)
        do_set_free(st)
      false ->
        do_push_to_waiting({:timeout, pdu}, st)
    end
  end

  def handle_pdu(pdu, st) do
    do_push_to_waiting({:pdu, pdu}, st)
  end

  def handle_stop(st) do
    case st.from do
      nil -> :nop
      from -> GenServer.reply(from, :stop)
    end
  end

  def handle_send_pdu_result(pdu, result, st) do
    case result do
      :ok -> st
      {:error, error} ->
        case Pdu.same?(pdu, st.pdu) and st.state == :wait_for_resp do
          true ->
            GenServer.reply(st.from, {:error, error})
            do_set_free(st)
          false ->
            do_push_to_waiting({:error, pdu, error}, st)
        end
    end
  end

  def do_push_to_waiting(pdu_info, st) do
    pdus = [pdu_info | st.additional_pdus]
    case st.state == :wait_for_pdus do
      true ->
        GenServer.reply(st.from, pdus)
        %{ do_set_free(st) | additional_pdus: [] }
      false ->
        %{ st | additional_pdus: pdus }
    end
  end

  defp do_set_free(st), do: %{ st | from: nil, pdu: nil, state: :free }

  defp do_get_pdus(st) do
    pdus = st.additional_pdus
    new_st = %{ st | additional_pdus: [] }
    {:reply, pdus, new_st}
  end

end


Fix another case of SSH channel deadlocking

defmodule Hypnotoad.Host.SSH.Channel do

  alias Hypnotoad.Host.SSH
  use GenServer.Behaviour
  use Hypnotoad.Common
  alias Hypnotoad.Semaphore, as: S

  def start_link(conn) do
    :gen_server.start_link __MODULE__, conn, []
  end

  def execute(pid, cmd, ref) do
  	:gen_server.call(pid, {:execute, cmd, ref}, :infinity)
  end

  def upload(pid, file, content, ref) do
    :gen_server.call(pid, {:upload, file, content, ref}, :infinity)
  end

  def download(pid, file, ref) do
    :gen_server.call(pid, {:download, file, ref}, :infinity)
  end

  defrecordp :state, connection: nil, from: nil, data: "", status: 0, splitter: nil, ref: nil, channel: nil

  def init(SSH[] = ssh) do
    {:ok, state(connection: ssh)}
  end

  def handle_call({:execute, cmd, ref}, from, state(connection: SSH[user: user, _connection: conn]) = s) do
    splitter = "#{:erlang.phash2(:erlang.now)}"
    file_content = """
    echo -n #{splitter}
    #{cmd}
    """
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # (( #{unless user == "root", do: "sudo"}
    #{cmd}
    # ))
    """
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, sftp_channel} = :ssh_sftp.start_channel(conn)
    :ok = :ssh_sftp.write_file(sftp_channel, Path.join(["/", "tmp", splitter]), file_content)
    :ssh_sftp.stop_channel(sftp_channel)
    S.unlock({Hypnotoad.Host.SSH.Connection, conn})
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, ch} = :ssh_connection.session_channel(conn, :infinity)
    unless user == "root" do
    	:ssh_connection.exec(conn, ch, String.to_char_list!("sudo sh -c 'sh /tmp/#{splitter} ; echo $? > /tmp/#{splitter}' ; E=`cat /tmp/#{splitter}` ; rm -f /tmp/#{splitter}; exit $E"), :infinity)
    else
      :ssh_connection.exec(conn, ch, String.to_char_list!("sh /tmp/#{splitter} ; E=$? ; rm -f /tmp/#{splitter}; exit $E"), :infinity)
    end
  	{:noreply, state(s, from: from, splitter: splitter, ref: ref, channel: ch)}
  end

  def handle_call({:upload, file, content, ref}, _from, state(connection: SSH[_connection: conn] = ssh) = s) do
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # Uploading #{file}
    """
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, sftp_channel} = :ssh_sftp.start_channel(conn)
    case :ssh_sftp.write_file(sftp_channel, file, content) do
      :ok ->
        :ssh_sftp.stop_channel(sftp_channel)
        S.unlock({Hypnotoad.Host.SSH.Connection, conn})
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file} complete
        """
        {:reply, :ok, s}
      {:error, :permission_denied} ->
        :ssh_sftp.stop_channel(sftp_channel)
        S.unlock({Hypnotoad.Host.SSH.Connection, conn})
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file}, permission denied, re-trying
        """
        {:ok, pid} = start_link(ssh)
        {0, home} = execute(pid, "sudo -u $SUDO_USER sh -c 'echo $HOME'", ref)
        tmp = "#{:erlang.phash2(:erlang.now)}"
        new_path = Path.join(home, ".hypnotoad.#{tmp}")
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file} to #{new_path} (temporarily)
        """
        S.lock({Hypnotoad.Host.SSH.Connection, conn})
        {:ok, sftp_channel} = :ssh_sftp.start_channel(conn)
        :ok = :ssh_sftp.write_file(sftp_channel, new_path, content)
        S.unlock({Hypnotoad.Host.SSH.Connection, conn})
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file}, moving from #{new_path}
        """
        {:ok, pid} = start_link(ssh)
        execute(pid, "mv -f #{new_path} #{file}", ref)
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file} complete
        """
        {:reply, :ok, s}
    end
  end

  def handle_call({:download, file, ref}, _from, state(connection: SSH[_connection: conn]) = s) do
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # Downloading #{file}
    """
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, sftp_channel} = :ssh_sftp.start_channel(conn)
    {:ok, data} = :ssh_sftp.read_file(sftp_channel, file)
    :ssh_sftp.stop_channel(sftp_channel)
    S.unlock({Hypnotoad.Host.SSH.Connection, conn})
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # Downloading #{file} complete
    """
    {:reply, data, s}
  end

  def handle_info({:ssh_cm, _, {:data, _, _, new_data}}, state(data: data, splitter: splitter, ref: ref) = s) do
    new_data = String.split(new_data, splitter) |> Enum.reverse |> hd
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, new_data)
  	{:noreply, state(s, data: data <> new_data)}
  end

  def handle_info({:ssh_cm, _, {:exit_status, _, status}}, state(ref: ref) = s) do
    if status == 0 do
      :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
      # EXIT CODE #{status}
      """
    else
      :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
      # EXIT CODE #{status}
      """
    end
  	{:noreply, state(s, status: status)}
  end

  def handle_info({:ssh_cm, _, {:closed, _}}, state(connection: SSH[_connection: conn], channel: ch, from: from, data: data, status: status) = s) do
  	data = String.strip(data)
    :gen_server.reply(from, {status, data})
    :ssh_connection.close(conn, ch)
    S.unlock({Hypnotoad.Host.SSH.Connection, conn})
    {:stop, :normal, s}
  end

  def handle_info({:ssh_cm, _, _}, state() = s) do
  	{:noreply, s}
  end

end

defmodule Hypnotoad.Host.SSH.Channel.Sup do
  use Supervisor.Behaviour

  def start_link do
    :supervisor.start_link({:local, __MODULE__}, __MODULE__, [])
  end

  def init([]) do
    children = [
      worker(Hypnotoad.Host.SSH.Channel, [], restart: :transient)
    ]
    supervise(children, strategy: :simple_one_for_one)
  end
end

defmodule Hypnotoad.Host.SSH.Channel do

  alias Hypnotoad.Host.SSH
  use GenServer.Behaviour
  use Hypnotoad.Common
  alias Hypnotoad.Semaphore, as: S

  def start_link(conn) do
    :gen_server.start_link __MODULE__, conn, []
  end

  def execute(pid, cmd, ref) do
  	:gen_server.call(pid, {:execute, cmd, ref}, :infinity)
  end

  def upload(pid, file, content, ref) do
    :gen_server.call(pid, {:upload, file, content, ref}, :infinity)
  end

  def download(pid, file, ref) do
    :gen_server.call(pid, {:download, file, ref}, :infinity)
  end

  defrecordp :state, connection: nil, from: nil, data: "", status: 0, splitter: nil, ref: nil

  def init(SSH[] = ssh) do
    {:ok, state(connection: ssh)}
  end

  def handle_call({:execute, cmd, ref}, from, state(connection: SSH[user: user, _connection: conn]) = s) do
    splitter = "#{:erlang.phash2(:erlang.now)}"
    file_content = """
    echo -n #{splitter}
    #{cmd}
    """
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # (( #{unless user == "root", do: "sudo"}
    #{cmd}
    # ))
    """
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, sftp_channel} = :ssh_sftp.start_channel(conn)
    :ok = :ssh_sftp.write_file(sftp_channel, Path.join(["/", "tmp", splitter]), file_content)
    :ssh_sftp.stop_channel(sftp_channel)
    S.unlock({Hypnotoad.Host.SSH.Connection, conn})
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, ch} = :ssh_connection.session_channel(conn, :infinity)
    unless user == "root" do
    	:ssh_connection.exec(conn, ch, String.to_char_list!("sudo sh -c 'sh /tmp/#{splitter} ; echo $? > /tmp/#{splitter}' ; E=`cat /tmp/#{splitter}` ; rm -f /tmp/#{splitter}; exit $E"), :infinity)
    else
      :ssh_connection.exec(conn, ch, String.to_char_list!("sh /tmp/#{splitter} ; E=$? ; rm -f /tmp/#{splitter}; exit $E"), :infinity)
    end
  	{:noreply, state(s, from: from, splitter: splitter, ref: ref)}
  end

  def handle_call({:upload, file, content, ref}, _from, state(connection: SSH[_connection: conn] = ssh) = s) do
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # Uploading #{file}
    """
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, sftp_channel} = :ssh_sftp.start_channel(conn)
    case :ssh_sftp.write_file(sftp_channel, file, content) do
      :ok ->
        :ssh_sftp.stop_channel(sftp_channel)
        S.unlock({Hypnotoad.Host.SSH.Connection, conn})
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file} complete
        """
        {:reply, :ok, s}
      {:error, :permission_denied} ->
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file}, permission denied, re-trying
        """
        {:ok, pid} = start_link(ssh)
        {0, home} = execute(pid, "sudo -u $SUDO_USER sh -c 'echo $HOME'", ref)
        tmp = "#{:erlang.phash2(:erlang.now)}"
        new_path = Path.join(home, ".hypnotoad.#{tmp}")
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file} to #{new_path} (temporarily)
        """
        :ok = :ssh_sftp.write_file(sftp_channel, new_path, content)
        :ssh_sftp.stop_channel(sftp_channel)
        S.unlock({Hypnotoad.Host.SSH.Connection, conn})
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file}, moving from #{new_path}
        """
        {:ok, pid} = start_link(ssh)
        execute(pid, "mv -f #{new_path} #{file}", ref)
        :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
        # Uploading #{file} complete
        """
        {:reply, :ok, s}
    end
  end

  def handle_call({:download, file, ref}, _from, state(connection: SSH[_connection: conn]) = s) do
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # Downloading #{file}
    """
    S.lock({Hypnotoad.Host.SSH.Connection, conn})
    {:ok, sftp_channel} = :ssh_sftp.start_channel(conn)
    {:ok, data} = :ssh_sftp.read_file(sftp_channel, file)
    :ssh_sftp.stop_channel(sftp_channel)
    S.unlock({Hypnotoad.Host.SSH.Connection, conn})
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
    # Downloading #{file} complete
    """
    {:reply, data, s}
  end

  def handle_info({:ssh_cm, _, {:data, _, _, new_data}}, state(data: data, splitter: splitter, ref: ref) = s) do
    new_data = String.split(new_data, splitter) |> Enum.reverse |> hd
    :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, new_data)
  	{:noreply, state(s, data: data <> new_data)}
  end

  def handle_info({:ssh_cm, _, {:exit_status, _, status}}, state(ref: ref) = s) do
    if status == 0 do
      :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
      # EXIT CODE #{status}
      """
    else
      :gproc_ps.publish(:l, {Hypnotoad.Shell, ref}, """)
      # EXIT CODE #{status}
      """
    end
  	{:noreply, state(s, status: status)}
  end

  def handle_info({:ssh_cm, _, {:closed, _}}, state(connection: SSH[_connection: conn], from: from, data: data, status: status) = s) do
  	data = String.strip(data)
    :gen_server.reply(from, {status, data})
    S.unlock({Hypnotoad.Host.SSH.Connection, conn})
    {:stop, :normal, s}
  end

  def handle_info({:ssh_cm, _, _}, state() = s) do
  	{:noreply, s}
  end

end

defmodule Hypnotoad.Host.SSH.Channel.Sup do
  use Supervisor.Behaviour

  def start_link do
    :supervisor.start_link({:local, __MODULE__}, __MODULE__, [])
  end

  def init([]) do
    children = [
      worker(Hypnotoad.Host.SSH.Channel, [], restart: :transient)
    ]
    supervise(children, strategy: :simple_one_for_one)
  end
end


catch nil cause

catch nil cause

defmodule Pan.Updater.Feed do
  alias Pan.Repo
  alias PanWeb.Feed
  import Pan.Parser.Helpers, only: [md5hash: 1, to_naive_datetime: 1]
  require Logger

  def needs_update(feed, podcast, forced \\ false) do
    if forced != false || feed.no_headers_available do
      {:ok, "go on"}
    else
      headers = [
        "User-Agent": "Mozilla/5.0 (compatible; Panoptikum; +https://panoptikum.io/)"
      ]

      options = [recv_timeout: 15_000, timeout: 15_000, hackney: [:insecure]]

      case HTTPoison.head(feed.self_link_url, headers, options) do
        {:ok, %HTTPoison.Response{headers: headers}} ->
          headermap = Enum.into(headers, %{})
          check_headers(podcast, feed, headermap["ETag"], headermap["Last-Modified"])

        {:error, _error} ->
          {:ok, "go on"}
      end
    end
  end

  defp check_headers(_, feed, nil, nil) do
    Feed.changeset(feed, %{no_headers_available: true})
    |> Repo.update()

    {:ok, "go on"}
  end

  defp check_headers(podcast, feed, nil, last_modified_header) do
    if last_modified_header != "" do
      last_modified = to_naive_datetime(last_modified_header)

      if feed.trust_last_modified and last_modified == feed.last_modified do
        # last_modified unchanged and trustworthy
        {:done, "nothing to do"}
      else
        check_trustwortyness(feed, podcast, last_modified)
      end
    else
      {:ok, "go on"}
    end
  end

  defp check_headers(_, feed, etag, _) do
    if etag == feed.etag do
      # etag unchanged
      {:done, "nothing to do"}
    else
      Feed.changeset(feed, %{etag: etag})
      |> Repo.update()

      {:ok, "go on"}
    end
  end

  defp check_trustwortyness(feed, podcast, last_modified) do
    # We allow for a difference of (rather arbitrary) 100 seconds between last_modified header
    # and the last build date of the podcast or the publishing date of latest episode:
    if (podcast.last_build_date &&
          abs(NaiveDateTime.diff(podcast.last_build_date, last_modified)) < 100) ||
         (podcast.latest_episode_publishing_date &&
            abs(NaiveDateTime.diff(podcast.latest_episode_publishing_date, last_modified)) < 100) do
      Feed.changeset(feed, %{last_modified: last_modified, trust_last_modified: true})
      |> Repo.update()

      {:done, "nothing to do"}
    else
      Feed.changeset(feed, %{last_modified: last_modified, trust_last_modified: false})
      |> Repo.update()

      {:ok, "go on"}
    end
  end

  def hash_changed(feed_xml, feed, forced) do
    case forced == false && feed.hash do
      false ->
        {:ok, "go on"}

      nil ->
        update_hash_and_go_on(feed, feed_xml)

      feed_hash ->
        if feed_hash == md5hash(feed_xml) do
          {:done, "nothing to do"}
        else
          update_hash_and_go_on(feed, feed_xml)
        end
    end
  end

  defp update_hash_and_go_on(feed, feed_xml) do
    Feed.changeset(feed, %{hash: md5hash(feed_xml)})
    |> Repo.update()

    {:ok, "go on"}
  end
end

defmodule Pan.Updater.Feed do
  alias Pan.Repo
  alias PanWeb.Feed
  import Pan.Parser.Helpers, only: [md5hash: 1, to_naive_datetime: 1]
  require Logger

  def needs_update(feed, podcast, forced \\ false) do
    if forced != false || feed.no_headers_available do
      {:ok, "go on"}
    else
      headers = [
        "User-Agent": "Mozilla/5.0 (compatible; Panoptikum; +https://panoptikum.io/)"
      ]

      options = [recv_timeout: 15_000, timeout: 15_000, hackney: [:insecure]]

      case HTTPoison.head(feed.self_link_url, headers, options) do
        {:ok, %HTTPoison.Response{headers: headers}} ->
          headermap = Enum.into(headers, %{})
          check_headers(podcast, feed, headermap["ETag"], headermap["Last-Modified"])

        {:error, _error} ->
          {:ok, "go on"}
      end
    end
  end

  defp check_headers(_, feed, nil, nil) do
    Feed.changeset(feed, %{no_headers_available: true})
    |> Repo.update()

    {:ok, "go on"}
  end

  defp check_headers(podcast, feed, nil, last_modified_header) do
    if last_modified_header != "" do
      last_modified = to_naive_datetime(last_modified_header)

      if feed.trust_last_modified and last_modified == feed.last_modified do
        # last_modified unchanged and trustworthy
        {:done, "nothing to do"}
      else
        check_trustwortyness(feed, podcast, last_modified)
      end
    else
      {:ok, "go on"}
    end
  end

  defp check_headers(_, feed, etag, _) do
    if etag == feed.etag do
      # etag unchanged
      {:done, "nothing to do"}
    else
      Feed.changeset(feed, %{etag: etag})
      |> Repo.update()

      {:ok, "go on"}
    end
  end

  defp check_trustwortyness(feed, podcast, last_modified) do
    # We allow for a difference of (rather arbitrary) 100 seconds between last_modified header
    # and the last build date of the podcast or the publishing date of latest episode:
    if (podcast.last_build_date &&
          abs(NaiveDateTime.diff(podcast.last_build_date, last_modified)) < 100) ||
         abs(NaiveDateTime.diff(podcast.latest_episode_publishing_date, last_modified)) < 100 do
      Feed.changeset(feed, %{last_modified: last_modified, trust_last_modified: true})
      |> Repo.update()

      {:done, "nothing to do"}
    else
      Feed.changeset(feed, %{last_modified: last_modified, trust_last_modified: false})
      |> Repo.update()

      {:ok, "go on"}
    end
  end

  def hash_changed(feed_xml, feed, forced) do
    case forced == false && feed.hash do
      false ->
        {:ok, "go on"}

      nil ->
        update_hash_and_go_on(feed, feed_xml)

      feed_hash ->
        if feed_hash == md5hash(feed_xml) do
          {:done, "nothing to do"}
        else
          update_hash_and_go_on(feed, feed_xml)
        end
    end
  end

  defp update_hash_and_go_on(feed, feed_xml) do
    Feed.changeset(feed, %{hash: md5hash(feed_xml)})
    |> Repo.update()

    {:ok, "go on"}
  end
end


We don’t need the preview flag here

We don’t need the preview flag here

defmodule Ello.V2.Manage.ArtistInviteController do
  use Ello.V2.Web, :controller
  alias Ello.Auth.{RequireUser}
  alias Ello.Core.Contest
  alias Ello.V2.{ArtistInviteView}

  plug RequireUser

  @doc """
  GET /v2/manage/artist_invites
  """
  def index(conn, _params) do
    artist_invites = Contest.my_artist_invites(standard_params(conn))
    conn
    |> api_render_if_stale(ArtistInviteView, :index, data: artist_invites)
  end
end

defmodule Ello.V2.Manage.ArtistInviteController do
  use Ello.V2.Web, :controller
  alias Ello.Auth.{RequireUser}
  alias Ello.Core.Contest
  alias Ello.V2.{ArtistInviteView}

  plug RequireUser

  @doc """
  GET /v2/manage/artist_invites
  """
  def index(conn, _params) do
    artist_invites = Contest.my_artist_invites(standard_params(conn, %{
      preview: false
    }))
    conn
    |> api_render_if_stale(ArtistInviteView, :index, data: artist_invites)
  end
end


example reference units

example reference units

defmodule Liquio.LandingController do
	use Liquio.Web, :controller

	plug :put_layout, "landing.html"

	def index(conn, _params) do
		examples = [
			%{
				poll: Poll.force_get("probability", "genetically modified foods are dangerous", ["science", "biology", "gmo"])
				|> Map.put(:fa_icon, "leaf"),
				references: []
			},
			%{
				poll: Poll.force_get("quantity", "number of refugees EU should let inside", ["politics", "eu", "refugees"])
				|> Map.put(:fa_icon, "question"),
				references: []
			},
			%{
				poll: Poll.force_get("quantity", "year when we will have artificial general intelligence", ["science", "artificial intelligence"])
				|> Map.put(:fa_icon, "bolt"),
				references: []
			},
			%{
				poll: Poll.force_get("probability", "global warming is caused by human activity", ["science", "nature", "global warming"])
				|> Map.put(:fa_icon, "sun-o"),
				references: [%{
					poll: Poll.force_get("time_quantity", "earth sea level yearly differences in cm", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "earth temperature in ℃", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "earth ocean temperature in ℃", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "earth ice volume yearly differences in km³", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "earth surface ocean waters acidity in pH", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("probability", "glaciers are retreating almost everywhere around the world", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("probability", "not enough earth temperature historical data available to know the cause of global warming", ["science", "nature", "global warming"]),
					for_choice: 0.0,
					references: []
				}, %{
					poll: Poll.force_get("probability", "climate models used to model global warming are proven to be unreliable", ["science", "nature", "global warming"]),
					for_choice: 0.0,
					references: []
				}]
			},
			%{
				poll: Poll.force_get("quantity", "additional tax revenue in USD if recreational cannabis becomes legal in California", ["california", "politics", "economics"])
				|> Map.put(:fa_icon, "bank"),
				references: []
			},
			%{
				poll: Poll.force_get("probability", "the theory of evolution is true", ["science", "biology", "evolution"])
				|> Map.put(:fa_icon, "hourglass-end"),
				references: []
			}
		]
		polls = %{
			:not_the_best_idea => Poll.force_get("probability", "vanilla ice cream flavor rating", ["joke"])
		}

		calculate_opts = get_calculation_opts_from_conn(conn)
		identity = Guardian.Plug.current_resource(conn)
		if identity != nil and Application.get_env(:liquio, :admin_identity_ids) |> Enum.member?(identity.id) do
			approve_references(examples, identity)
		end
		examples = Enum.map(examples, fn(example) ->
			example.poll
			|> Map.put(:results, Poll.calculate(example.poll, calculate_opts))
			|> Map.put(:num_references, Enum.count(example.references))
		end)
		|> Enum.sort_by(& &1.results.turnout_ratio + 0.1 * &1.num_references, &>=/2)

		render conn, "index.html", examples: examples, polls: polls
	end

	def approve_references(examples, identity) do
		Enum.flat_map(examples, fn(%{:poll => poll, :references => references}) ->
			Enum.each(references, &(cast_approve_vote(poll, &1, identity)))
			approve_references(references, identity)
		end)
	end

	def cast_approve_vote(poll, reference, identity) do
		reference = Reference.get(poll, reference.poll, reference.for_choice)
		|> Repo.preload([:approval_poll])

		current_vote = Vote.current_by(reference.approval_poll, identity)
		if current_vote == nil do
			Vote.set(reference.approval_poll, identity, %{:main => 1.0})
		end
	end
end

defmodule Liquio.LandingController do
	use Liquio.Web, :controller

	plug :put_layout, "landing.html"

	def index(conn, _params) do
		examples = [
			%{
				poll: Poll.force_get("probability", "genetically modified foods are dangerous", ["science", "biology", "gmo"])
				|> Map.put(:fa_icon, "leaf"),
				references: []
			},
			%{
				poll: Poll.force_get("quantity", "number of refugees EU should let inside", ["politics", "eu", "refugees"])
				|> Map.put(:fa_icon, "question"),
				references: []
			},
			%{
				poll: Poll.force_get("quantity", "year when we will have artificial general intelligence", ["science", "artificial intelligence"])
				|> Map.put(:fa_icon, "bolt"),
				references: []
			},
			%{
				poll: Poll.force_get("probability", "global warming is caused by human activity", ["science", "nature", "global warming"])
				|> Map.put(:fa_icon, "sun-o"),
				references: [%{
					poll: Poll.force_get("time_quantity", "earth sea level yearly differences in cm", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "earth temperature in ℃", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "earth ocean temperature in ℃", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "earth ice volume yearly differences in km³", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("time_quantity", "surface ocean waters acidity", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("probability", "glaciers are retreating almost everywhere around the world", ["science", "nature", "global warming"]),
					for_choice: 1.0,
					references: []
				}, %{
					poll: Poll.force_get("probability", "not enough earth temperature historical data available to know the cause of global warming", ["science", "nature", "global warming"]),
					for_choice: 0.0,
					references: []
				}, %{
					poll: Poll.force_get("probability", "climate models used to model global warming are proven to be unreliable", ["science", "nature", "global warming"]),
					for_choice: 0.0,
					references: []
				}]
			},
			%{
				poll: Poll.force_get("quantity", "additional tax revenue in USD if recreational cannabis becomes legal in California", ["california", "politics", "economics"])
				|> Map.put(:fa_icon, "bank"),
				references: []
			},
			%{
				poll: Poll.force_get("probability", "the theory of evolution is true", ["science", "biology", "evolution"])
				|> Map.put(:fa_icon, "hourglass-end"),
				references: []
			}
		]
		polls = %{
			:not_the_best_idea => Poll.force_get("probability", "vanilla ice cream flavor rating", ["joke"])
		}

		calculate_opts = get_calculation_opts_from_conn(conn)
		identity = Guardian.Plug.current_resource(conn)
		if identity != nil and Application.get_env(:liquio, :admin_identity_ids) |> Enum.member?(identity.id) do
			approve_references(examples, identity)
		end
		examples = Enum.map(examples, fn(example) ->
			example.poll
			|> Map.put(:results, Poll.calculate(example.poll, calculate_opts))
			|> Map.put(:num_references, Enum.count(example.references))
		end)
		|> Enum.sort_by(& &1.results.turnout_ratio + 0.1 * &1.num_references, &>=/2)

		render conn, "index.html", examples: examples, polls: polls
	end

	def approve_references(examples, identity) do
		Enum.flat_map(examples, fn(%{:poll => poll, :references => references}) ->
			Enum.each(references, &(cast_approve_vote(poll, &1, identity)))
			approve_references(references, identity)
		end)
	end

	def cast_approve_vote(poll, reference, identity) do
		reference = Reference.get(poll, reference.poll, reference.for_choice)
		|> Repo.preload([:approval_poll])

		current_vote = Vote.current_by(reference.approval_poll, identity)
		if current_vote == nil do
			Vote.set(reference.approval_poll, identity, %{:main => 1.0})
		end
	end
end


stab at unit test, let's see what CI says cc @devoncestes

defmodule Benchee.MemoryMeasureTest do
  # We cannot use async: true because of the test that we're running to ensure
  # there aren't any leaked processes if functions fail while we're tracing
  # them.
  use ExUnit.Case
  alias Benchee.Benchmark.Measure.Memory
  import ExUnit.CaptureIO

  @moduletag :memory_measure

  describe "measure/1" do
    test "returns the result of the function and the memory used (in bytes)" do
      fun_to_run = fn -> Enum.to_list(1..10) end
      assert {memory_used, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]} = Memory.measure(fun_to_run)
      # We need to have some wiggle room here because memory used varies from
      # system to system. It's consistent in an environment, but changes
      # between environments.
      assert memory_used > 350
      assert memory_used < 380
    end

    test "doesn't return broken values" do
      fun = fn -> BenchKeyword.delete_v0(Enum.map(1..100, &{:"k#{&1}", &1}), :k100) end
      assert {memory_used, _} = Memory.measure(fun)

      assert memory_used >= 5_000
      assert memory_used <= 10_000
    end

    test "will not leak processes if the applied function raises an exception" do
      starting_processes = Enum.count(Process.list())

      # We're capturing the IO here so the error output doesn't clog up our
      # tests. We don't want stuff in our green dots!!
      # We also need to sleep for 1ms because the error output is coming from
      # a separate process, so we need to wait for that to emit so we can
      # capture it.
      capture_io(fn ->
        Memory.measure(fn -> exit(:kill) end)
        Process.sleep(1)
      end)

      assert Enum.count(Process.list()) == starting_processes
    end
  end
end

defmodule Benchee.MemoryMeasureTest do
  # We cannot use async: true because of the test that we're running to ensure
  # there aren't any leaked processes if functions fail while we're tracing
  # them.
  use ExUnit.Case
  alias Benchee.Benchmark.Measure.Memory
  import ExUnit.CaptureIO

  @moduletag :memory_measure

  describe "measure/1" do
    test "returns the result of the function and the memory used (in bytes)" do
      fun_to_run = fn -> Enum.to_list(1..10) end
      assert {memory_used, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]} = Memory.measure(fun_to_run)
      # We need to have some wiggle room here because memory used varies from
      # system to system. It's consistent in an environment, but changes
      # between environments.
      assert memory_used > 350
      assert memory_used < 380
    end

    test "will not leak processes if the applied function raises an exception" do
      starting_processes = Enum.count(Process.list())

      # We're capturing the IO here so the error output doesn't clog up our
      # tests. We don't want stuff in our green dots!!
      # We also need to sleep for 1ms because the error output is coming from
      # a separate process, so we need to wait for that to emit so we can
      # capture it.
      capture_io(fn ->
        Memory.measure(fn -> exit(:kill) end)
        Process.sleep(1)
      end)

      assert Enum.count(Process.list()) == starting_processes
    end
  end
end


Fix credo issues.

Fix credo issues.

This reverts commit 68584ce1d464470ab5e104e0cef78b8bbc2d49c0.

defmodule CoursePlanner.UserController do
  use CoursePlanner.Web, :controller
  alias CoursePlanner.User
  alias CoursePlanner.Router.Helpers
  alias Coherence.ControllerHelpers
  alias Ecto.DateTime
  require Logger

  def index(conn, _params) do
    query = from u in User, where: is_nil(u.deleted_at)
    users = Repo.all(query)
    render(conn, "index.html", users: users)
  end

  def new(conn, _params) do
    changeset = User.changeset(%User{})
    render(conn, "new.html", changeset: changeset)
  end

  def create(conn, %{"user" => user} = params) do
    email = user["email"]
    token = ControllerHelpers.random_string 48
    url = Helpers.password_url(conn, :edit, token)
    reset_params =
      %{"reset_password_token" => token,
        "reset_password_sent_at" => DateTime.utc,
        "password" => "fakepassword"}
    merged = Map.merge(user, reset_params)
    user = User.changeset(%User{}, merged, :create)
    case Repo.insert(user) do
      {:ok, user} ->
        ControllerHelpers.send_user_email :password, user, url
        conn
        |> put_flash(:info, "User created and notified by.")
        |> redirect(to: user_path(conn, :index))
      {:error, changeset} -> Logger.warn("something went wrong creating a new user: #{changeset}")
        conn
        |> put_flash(:error, "Something went wrong.")
        |> render("new.html", changeset: changeset)
    end
  end

  def show(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    render(conn, "show.html", user: user)
  end

  def edit(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user)
    render(conn, "edit.html", user: user, changeset: changeset)
  end

  def update(conn, %{"id" => id, "user" => user_params}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user, user_params)

    case Repo.update(changeset) do
      {:ok, user} ->
        conn
        |> put_flash(:info, "User updated successfully.")
        |> redirect(to: user_path(conn, :show, user))
      {:error, changeset} ->
        render(conn, "edit.html", user: user, changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user,
      %{deleted_at: DateTime.utc()},
      :delete)

    case Repo.update(changeset) do
      {:ok, user} ->
        conn
        |> put_flash(:info, "User deleted successfully.")
        |> redirect(to: user_path(conn, :index))
      {:error, changeset} ->
        conn
        |> put_flash(:error, "Something went wrong.")
        |> redirect(to: user_path(conn, :index))
    end
  end
end

defmodule CoursePlanner.UserController do
  use CoursePlanner.Web, :controller
  alias CoursePlanner.User
  alias CoursePlanner.Router.Helpers
  alias Coherence.ControllerHelpers
  alias Ecto.DateTime
  require Logger

  def index(conn, _params) do
    query = from u in User, where: is_nil(u.deleted_at)
    users = Repo.all(query)
    render(conn, "index.html", users: users)
  end

  def new(conn, _params) do
    changeset = User.changeset(%User{})
    render(conn, "new.html", changeset: changeset)
  end

  def create(conn, %{"user" => user} = params) do
    email = user["email"]
    token = ControllerHelpers.random_string 48
    url = Helpers.password_url(conn, :edit, token)
    reset_params =
      %{"reset_password_token" => token,
        "reset_password_sent_at" => DateTime.utc,
        "password" => "fakepassword"}
    merged = Map.merge(user, reset_params)
    user = User.changeset(%User{}, merged, :create)
    case Repo.insert(user) do
      {:ok, user} ->
        ControllerHelpers.send_user_email :password, user, url
        conn
        |> put_flash(:info, "User created and notified by.")
        |> redirect(to: user_path(conn, :index))
      {:error, changeset} -> Logger.warn("something went wrong creating a new user: #{changeset}")
        conn
        |> put_flash(:error, "Something went wrong.")
        |> render("new.html", changeset: changeset)
    end
  end

  def show(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    render(conn, "show.html", user: user)
  end

  def edit(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user)
    render(conn, "edit.html", user: user, changeset: changeset)
  end

  def update(conn, %{"id" => id, "user" => user_params}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user, user_params)

    case Repo.update(changeset) do
      {:ok, user} ->
        conn
        |> put_flash(:info, "User updated successfully.")
        |> redirect(to: user_path(conn, :show, user))
      {:error, changeset} ->
        render(conn, "edit.html", user: user, changeset: changeset)
    end
  end

  def delete(conn, %{"id" => id}) do
    user = Repo.get!(User, id)
    changeset = User.changeset(user,
      %{deleted_at: Ecto.DateTime.utc()},
      :delete)

    IO.inspect changeset
    case Repo.update(changeset) do
      {:ok, user} ->
        conn
        |> put_flash(:info, "User deleted successfully.")
        |> redirect(to: user_path(conn, :index))
      {:error, changeset} ->
        conn
        |> put_flash(:error, "Something went wrong.")
        |> redirect(to: user_path(conn, :index))
    end
  end
end


Make plurals plural

Make plurals plural

defmodule OpenPantry.FoodSelectionTest do
  use OpenPantry.AcceptanceCase, async: true
  import OpenPantry.Factory

  def complete_facility do
    facility = insert(:facility)
    user = insert(:user, facility: facility)
    food_group1 = insert(:food_group)
    food1 = insert(:food, food_group: food_group1)
    food_group2 = insert(:food_group)
    food2 = insert(:food, food_group: food_group2)
    stock1 = insert(:stock, facility: facility, food: food1)
    stock2 = insert(:stock, facility: facility, food: food2)
    credit_type1 = insert(:credit_type, food_groups: [food_group1])
    credit_type2 = insert(:credit_type, food_groups: [food_group2])
    insert(:user_credit, credit_type: credit_type1, user: user )
    insert(:user_credit, credit_type: credit_type2, user: user )
    %{credit_types: [credit_type1, credit_type2], facility: facility, user: user, stocks: [stock1, stock2], foods: [food1, food2], food_groups: [food_group1, food_group2]}
  end

  @tag timeout: Ownership.timeout
  test "selection table has tab per credit type, plus meals and cart", %{session: session} do
    %{credit_types: [credit_type|_]} = complete_facility()

    first_credit = session
    |> visit("/en/food_selections")
    |> find("##{credit_type.name}")
    |> text

    assert first_credit =~ ~r/#{credit_type.name}/
  end
end

defmodule OpenPantry.FoodSelectionTest do
  use OpenPantry.AcceptanceCase, async: true
  import OpenPantry.Factory

  def complete_facility do
    facility = insert(:facility)
    user = insert(:user, facility: facility)
    food_group = insert(:food_group)
    food = insert(:food, food_group: food_group)
    stock = insert(:stock, facility: facility, food: food)
    credit_type = insert(:credit_type, food_groups: [food_group])
    insert(:user_credit, credit_type: credit_type, user: user )
    %{credit_type: credit_type, facility: facility, user: user, stock: stock, food: food, food_group: food_group}
  end

  @tag timeout: Ownership.timeout
  test "selection table has tab per credit type, plus meals and cart", %{session: session} do
    %{credit_type: credit_type} = complete_facility()

    first_credit = session
    |> visit("/en/food_selections")
    |> find("##{credit_type.name}")
    |> text

    assert first_credit =~ ~r/#{credit_type.name}/
  end
end


Remove extra newline in generated context test cases (#3129)

Currently, context tests generated by running the html generator (`mix
phx.gen.context Accounts User users name:string`), produce a test with
an extra line with some trailing whitespace (illustrated with dots
here):

        test "update_user/2 with valid data updates the user" do
          user = user_fixture()
          assert {:ok, %User{} = user} = Accounts.update_user(user, @update_attrs)

    ······
          assert user.name == "some updated name"
        end

This patch removes both newlines from the template, resulting in a
generated test like this:

        test "update_user/2 with valid data updates the user" do
          user = user_fixture()
          assert {:ok, %User{} = user} = Accounts.update_user(user, @update_attrs)
          assert user.name == "some updated name"
        end

  describe "<%= schema.plural %>" do
    alias <%= inspect schema.module %>

    @valid_attrs <%= inspect schema.params.create %>
    @update_attrs <%= inspect schema.params.update %>
    @invalid_attrs <%= inspect for {key, _} <- schema.params.create, into: %{}, do: {key, nil} %>

    def <%= schema.singular %>_fixture(attrs \\ %{}) do
      {:ok, <%= schema.singular %>} =
        attrs
        |> Enum.into(@valid_attrs)
        |> <%= inspect context.alias %>.create_<%= schema.singular %>()

      <%= schema.singular %>
    end

    test "list_<%= schema.plural %>/0 returns all <%= schema.plural %>" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert <%= inspect context.alias %>.list_<%= schema.plural %>() == [<%= schema.singular %>]
    end

    test "get_<%= schema.singular %>!/1 returns the <%= schema.singular %> with given id" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id) == <%= schema.singular %>
    end

    test "create_<%= schema.singular %>/1 with valid data creates a <%= schema.singular %>" do
      assert {:ok, %<%= inspect schema.alias %>{} = <%= schema.singular %>} = <%= inspect context.alias %>.create_<%= schema.singular %>(@valid_attrs)<%= for {field, value} <- schema.params.create do %>
      assert <%= schema.singular %>.<%= field %> == <%= Mix.Phoenix.Schema.value(schema, field, value) %><% end %>
    end

    test "create_<%= schema.singular %>/1 with invalid data returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = <%= inspect context.alias %>.create_<%= schema.singular %>(@invalid_attrs)
    end

    test "update_<%= schema.singular %>/2 with valid data updates the <%= schema.singular %>" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert {:ok, %<%= inspect schema.alias %>{} = <%= schema.singular %>} = <%= inspect context.alias %>.update_<%= schema.singular %>(<%= schema.singular %>, @update_attrs)<%= for {field, value} <- schema.params.update do %>
      assert <%= schema.singular %>.<%= field %> == <%= Mix.Phoenix.Schema.value(schema, field, value) %><% end %>
    end

    test "update_<%= schema.singular %>/2 with invalid data returns error changeset" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert {:error, %Ecto.Changeset{}} = <%= inspect context.alias %>.update_<%= schema.singular %>(<%= schema.singular %>, @invalid_attrs)
      assert <%= schema.singular %> == <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id)
    end

    test "delete_<%= schema.singular %>/1 deletes the <%= schema.singular %>" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert {:ok, %<%= inspect schema.alias %>{}} = <%= inspect context.alias %>.delete_<%= schema.singular %>(<%= schema.singular %>)
      assert_raise Ecto.NoResultsError, fn -> <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id) end
    end

    test "change_<%= schema.singular %>/1 returns a <%= schema.singular %> changeset" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert %Ecto.Changeset{} = <%= inspect context.alias %>.change_<%= schema.singular %>(<%= schema.singular %>)
    end
  end

  describe "<%= schema.plural %>" do
    alias <%= inspect schema.module %>

    @valid_attrs <%= inspect schema.params.create %>
    @update_attrs <%= inspect schema.params.update %>
    @invalid_attrs <%= inspect for {key, _} <- schema.params.create, into: %{}, do: {key, nil} %>

    def <%= schema.singular %>_fixture(attrs \\ %{}) do
      {:ok, <%= schema.singular %>} =
        attrs
        |> Enum.into(@valid_attrs)
        |> <%= inspect context.alias %>.create_<%= schema.singular %>()

      <%= schema.singular %>
    end

    test "list_<%= schema.plural %>/0 returns all <%= schema.plural %>" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert <%= inspect context.alias %>.list_<%= schema.plural %>() == [<%= schema.singular %>]
    end

    test "get_<%= schema.singular %>!/1 returns the <%= schema.singular %> with given id" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id) == <%= schema.singular %>
    end

    test "create_<%= schema.singular %>/1 with valid data creates a <%= schema.singular %>" do
      assert {:ok, %<%= inspect schema.alias %>{} = <%= schema.singular %>} = <%= inspect context.alias %>.create_<%= schema.singular %>(@valid_attrs)<%= for {field, value} <- schema.params.create do %>
      assert <%= schema.singular %>.<%= field %> == <%= Mix.Phoenix.Schema.value(schema, field, value) %><% end %>
    end

    test "create_<%= schema.singular %>/1 with invalid data returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = <%= inspect context.alias %>.create_<%= schema.singular %>(@invalid_attrs)
    end

    test "update_<%= schema.singular %>/2 with valid data updates the <%= schema.singular %>" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert {:ok, %<%= inspect schema.alias %>{} = <%= schema.singular %>} = <%= inspect context.alias %>.update_<%= schema.singular %>(<%= schema.singular %>, @update_attrs)

      <%= for {field, value} <- schema.params.update do %>
      assert <%= schema.singular %>.<%= field %> == <%= Mix.Phoenix.Schema.value(schema, field, value) %><% end %>
    end

    test "update_<%= schema.singular %>/2 with invalid data returns error changeset" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert {:error, %Ecto.Changeset{}} = <%= inspect context.alias %>.update_<%= schema.singular %>(<%= schema.singular %>, @invalid_attrs)
      assert <%= schema.singular %> == <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id)
    end

    test "delete_<%= schema.singular %>/1 deletes the <%= schema.singular %>" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert {:ok, %<%= inspect schema.alias %>{}} = <%= inspect context.alias %>.delete_<%= schema.singular %>(<%= schema.singular %>)
      assert_raise Ecto.NoResultsError, fn -> <%= inspect context.alias %>.get_<%= schema.singular %>!(<%= schema.singular %>.id) end
    end

    test "change_<%= schema.singular %>/1 returns a <%= schema.singular %> changeset" do
      <%= schema.singular %> = <%= schema.singular %>_fixture()
      assert %Ecto.Changeset{} = <%= inspect context.alias %>.change_<%= schema.singular %>(<%= schema.singular %>)
    end
  end


Remove duplicated migration

Remove duplicated migration

defmodule CoursePlanner.Repo.Migrations.NilifyAllTaskVolunteer do
  use Ecto.Migration

  def change do
    drop_if_exists index(:tasks, [:user_id])
    alter table(:tasks) do
      remove :user_id
      add :user_id, references(:users, on_delete: :nilify_all)
    end
  end
end


ensures adapter is completely unloaded

ensures adapter is completely unloaded

defmodule Geolix.Adapter.MMDB2.Database.LoaderTest do
  use ExUnit.Case, async: true

  alias Geolix.Adapter.MMDB2
  alias Geolix.Result

  @fixture_path [__DIR__, "../fixtures"] |> Path.join() |> Path.expand()

  test "compressed databases" do
    ip = {1, 1, 1, 1}
    expected = %{ip_address: ip, type: "test"}
    databases = [:testdata_gz, :testdata_plain, :testdata_tar, :testdata_targz]

    Enum.each(databases, &assert(Geolix.lookup(ip, where: &1) == expected))
  end

  test "reloading a database" do
    path_city = Path.join([@fixture_path, "GeoIP2-City-Test.mmdb"])
    path_country = Path.join([@fixture_path, "GeoIP2-Country-Test.mmdb"])

    db_city = %{id: :reload, adapter: MMDB2, source: path_city}
    db_country = %{id: :reload, adapter: MMDB2, source: path_country}

    assert :ok = Geolix.load_database(db_city)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :reload)

    assert :ok = Geolix.load_database(db_country)
    assert %Result.Country{} = Geolix.lookup("2.125.160.216", where: :reload)
  end

  test "system environment configuration" do
    path = Path.join([@fixture_path, "GeoIP2-City-Test.mmdb"])
    var = "GEOLIX_TEST_DATABASE_PATH"
    db = %{id: :system_env, adapter: MMDB2, source: {:system, var}}

    System.put_env(var, path)

    assert :ok = Geolix.load_database(db)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :system_env)

    System.delete_env(var)
    Geolix.unload_database(:system_env)
  end

  test "system environment configuration (default value)" do
    path = Path.join([@fixture_path, "GeoIP2-City-Test.mmdb"])
    var = "GEOLIX_TEST_DATABASE_PATH"
    db = %{id: :system_env_default, adapter: MMDB2, source: {:system, var, path}}

    System.delete_env(var)

    assert :ok = Geolix.load_database(db)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :system_env_default)

    Geolix.unload_database(:system_env_default)

    refute Geolix.lookup("2.125.160.216", where: :system_env_default)
  end

  test "remote database" do
    # setup internal testing webserver
    Application.ensure_all_started(:inets)

    httpd_opts = [
      document_root: String.to_charlist(@fixture_path),
      port: 0,
      server_name: 'geolix_test',
      server_root: String.to_charlist(@fixture_path)
    ]

    {:ok, httpd_pid} = :inets.start(:httpd, httpd_opts)

    # test remote file loading
    port = :httpd.info(httpd_pid)[:port]
    path = "http://localhost:#{port}/GeoIP2-City-Test.mmdb"
    db = %{id: :remote, adapter: MMDB2, source: path}

    assert :ok = Geolix.load_database(db)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :remote)
  end

  test "error if database contains no metadata" do
    path = Path.join([@fixture_path, ".gitignore"])
    db = %{id: :invalid, adapter: MMDB2, source: path}

    assert {:error, :no_metadata} == Geolix.load_database(db)
  end

  test "database with invalid filename (not found)" do
    db = %{id: :unknown_database, adapter: MMDB2, source: "invalid"}

    assert {:error, :enoent} = Geolix.load_database(db)
  end

  test "database with invalid filename (remote not found)" do
    db = %{id: :unknown_database, adapter: MMDB2, source: "http://does.not.exist/"}
    err = Geolix.load_database(db)

    assert {:error, {:remote, {:failed_connect, _}}} = err
  end
end

defmodule Geolix.Adapter.MMDB2.Database.LoaderTest do
  use ExUnit.Case, async: true

  alias Geolix.Adapter.MMDB2
  alias Geolix.Result

  @fixture_path [__DIR__, "../fixtures"] |> Path.join() |> Path.expand()

  test "compressed databases" do
    ip = {1, 1, 1, 1}
    expected = %{ip_address: ip, type: "test"}
    databases = [:testdata_gz, :testdata_plain, :testdata_tar, :testdata_targz]

    Enum.each(databases, &assert(Geolix.lookup(ip, where: &1) == expected))
  end

  test "reloading a database" do
    path_city = Path.join([@fixture_path, "GeoIP2-City-Test.mmdb"])
    path_country = Path.join([@fixture_path, "GeoIP2-Country-Test.mmdb"])

    db_city = %{id: :reload, adapter: MMDB2, source: path_city}
    db_country = %{id: :reload, adapter: MMDB2, source: path_country}

    assert :ok = Geolix.load_database(db_city)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :reload)

    assert :ok = Geolix.load_database(db_country)
    assert %Result.Country{} = Geolix.lookup("2.125.160.216", where: :reload)
  end

  test "system environment configuration" do
    path = Path.join([@fixture_path, "GeoIP2-City-Test.mmdb"])
    var = "GEOLIX_TEST_DATABASE_PATH"
    db = %{id: :system_env, adapter: MMDB2, source: {:system, var}}

    System.put_env(var, path)

    assert :ok = Geolix.load_database(db)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :system_env)

    System.delete_env(var)
    Geolix.unload_database(:system_env)
  end

  test "system environment configuration (default value)" do
    path = Path.join([@fixture_path, "GeoIP2-City-Test.mmdb"])
    var = "GEOLIX_TEST_DATABASE_PATH"
    db = %{id: :system_env_default, adapter: MMDB2, source: {:system, var, path}}

    System.delete_env(var)

    assert :ok = Geolix.load_database(db)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :system_env_default)

    Geolix.unload_database(:system_env_default)
  end

  test "remote database" do
    # setup internal testing webserver
    Application.ensure_all_started(:inets)

    httpd_opts = [
      document_root: String.to_charlist(@fixture_path),
      port: 0,
      server_name: 'geolix_test',
      server_root: String.to_charlist(@fixture_path)
    ]

    {:ok, httpd_pid} = :inets.start(:httpd, httpd_opts)

    # test remote file loading
    port = :httpd.info(httpd_pid)[:port]
    path = "http://localhost:#{port}/GeoIP2-City-Test.mmdb"
    db = %{id: :remote, adapter: MMDB2, source: path}

    assert :ok = Geolix.load_database(db)
    assert %Result.City{} = Geolix.lookup("2.125.160.216", where: :remote)
  end

  test "error if database contains no metadata" do
    path = Path.join([@fixture_path, ".gitignore"])
    db = %{id: :invalid, adapter: MMDB2, source: path}

    assert {:error, :no_metadata} == Geolix.load_database(db)
  end

  test "database with invalid filename (not found)" do
    db = %{id: :unknown_database, adapter: MMDB2, source: "invalid"}

    assert {:error, :enoent} = Geolix.load_database(db)
  end

  test "database with invalid filename (remote not found)" do
    db = %{id: :unknown_database, adapter: MMDB2, source: "http://does.not.exist/"}
    err = Geolix.load_database(db)

    assert {:error, {:remote, {:failed_connect, _}}} = err
  end
end


Inline filter results

Inline filter results

Code.require_file("../test_helper.exs", __DIR__)

defmodule ExUnit.FiltersTest do
  use ExUnit.Case, async: true

  doctest ExUnit.Filters

  test "evaluating filters" do
    assert ExUnit.Filters.eval([], [:os], %{}, []) == :ok
    assert ExUnit.Filters.eval([], [os: :win], %{os: :unix}, []) == :ok
    assert ExUnit.Filters.eval([], [:os], %{os: :unix}, []) == {:error, "due to os filter"}
    assert ExUnit.Filters.eval([], [os: :unix], %{os: :unix}, []) == {:error, "due to os filter"}

    assert ExUnit.Filters.eval([os: :win], [], %{}, []) == :ok
    assert ExUnit.Filters.eval([os: :win], [], %{os: :unix}, []) == :ok
    assert ExUnit.Filters.eval([os: :win], [:os], %{}, []) == :ok
    assert ExUnit.Filters.eval([os: :win], [:os], %{os: :win}, []) == :ok

    assert ExUnit.Filters.eval([os: :win, os: :unix], [:os], %{os: :win}, []) == :ok
  end

  test "evaluating filters with skip" do
    assert ExUnit.Filters.eval([], [], %{}, []) == :ok
    assert ExUnit.Filters.eval([], [], %{skip: true}, []) == {:error, "due to skip tag"}
    assert ExUnit.Filters.eval([], [], %{skip: "skipped"}, []) == {:error, "skipped"}
    assert ExUnit.Filters.eval([], [:os], %{skip: "skipped"}, []) == {:error, "skipped"}
    assert ExUnit.Filters.eval([:skip], [], %{skip: true}, []) == :ok
    assert ExUnit.Filters.eval([:skip], [], %{skip: "skipped"}, []) == :ok
  end

  test "evaluating filters matches integers" do
    assert ExUnit.Filters.eval([int: "1"], [], %{int: 1}, []) == :ok
    assert ExUnit.Filters.eval([int: "1"], [int: 5], %{int: 1}, []) == :ok
    assert ExUnit.Filters.eval([int: "1"], [:int], %{int: 1}, []) == :ok
  end

  test "evaluating filter matches atoms" do
    assert ExUnit.Filters.eval([os: "win"], [], %{os: :win}, []) == :ok
    assert ExUnit.Filters.eval([os: "win"], [os: :unix], %{os: :win}, []) == :ok
    assert ExUnit.Filters.eval([os: "win"], [:os], %{os: :win}, []) == :ok
    assert ExUnit.Filters.eval([module: "Foo"], [:os], %{module: Foo}, []) == :ok
  end

  test "evaluating filter matches regexes" do
    assert ExUnit.Filters.eval([os: ~r"win"], [], %{os: :win}, []) == :ok

    assert ExUnit.Filters.eval([os: ~r"mac"], [os: :unix], %{os: :unix}, []) ==
             {:error, "due to os filter"}
  end

  test "evaluating filter uses special rules for line" do
    tests = [
      %ExUnit.Test{tags: %{line: 3, describe_line: 2}},
      %ExUnit.Test{tags: %{line: 5, describe_line: nil}},
      %ExUnit.Test{tags: %{line: 8, describe_line: 7}},
      %ExUnit.Test{tags: %{line: 10, describe_line: 7}},
      %ExUnit.Test{tags: %{line: 13, describe_line: 12}}
    ]

    assert ExUnit.Filters.eval([line: "3"], [:line], %{line: 3, describe_line: 2}, tests) == :ok
    assert ExUnit.Filters.eval([line: "4"], [:line], %{line: 3, describe_line: 2}, tests) == :ok
    assert ExUnit.Filters.eval([line: "5"], [:line], %{line: 5, describe_line: nil}, tests) == :ok
    assert ExUnit.Filters.eval([line: "6"], [:line], %{line: 5, describe_line: nil}, tests) == :ok
    assert ExUnit.Filters.eval([line: "2"], [:line], %{line: 3, describe_line: 2}, tests) == :ok
    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 8, describe_line: 7}, tests) == :ok
    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 10, describe_line: 7}, tests) == :ok

    assert ExUnit.Filters.eval([line: "1"], [:line], %{line: 3, describe_line: 2}, tests) ==
             {:error, "due to os filter"}

    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 3, describe_line: 2}, tests) ==
             {:error, "due to os filter"}

    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 5, describe_line: nil}, tests) ==
             {:error, "due to os filter"}
  end

  test "parsing filters" do
    assert ExUnit.Filters.parse(["run"]) == [:run]
    assert ExUnit.Filters.parse(["run:true"]) == [run: "true"]
    assert ExUnit.Filters.parse(["run:test"]) == [run: "test"]
    assert ExUnit.Filters.parse(["line:9"]) == [line: "9"]
  end

  test "file paths with line numbers" do
    assert ExUnit.Filters.parse_path("test/some/path.exs:123") ==
             {"test/some/path.exs", [exclude: [:test], include: [line: "123"]]}

    assert ExUnit.Filters.parse_path("test/some/path.exs") == {"test/some/path.exs", []}

    assert ExUnit.Filters.parse_path("test/some/path.exs:123notreallyalinenumber123") ==
             {"test/some/path.exs:123notreallyalinenumber123", []}

    assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123") ==
             {"C:\\some\\path.exs", [exclude: [:test], include: [line: "123"]]}

    assert ExUnit.Filters.parse_path("C:\\some\\path.exs") == {"C:\\some\\path.exs", []}

    assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123notreallyalinenumber123") ==
             {"C:\\some\\path.exs:123notreallyalinenumber123", []}
  end
end

Code.require_file("../test_helper.exs", __DIR__)

defmodule ExUnit.FiltersTest do
  use ExUnit.Case, async: true

  doctest ExUnit.Filters

  test "evaluating filters" do
    assert ExUnit.Filters.eval([], [:os], %{}, []) == :ok
    assert ExUnit.Filters.eval([], [os: :win], %{os: :unix}, []) == :ok
    assert ExUnit.Filters.eval([], [:os], %{os: :unix}, []) == {:error, "due to os filter"}
    assert ExUnit.Filters.eval([], [os: :unix], %{os: :unix}, []) == {:error, "due to os filter"}

    assert ExUnit.Filters.eval([os: :win], [], %{}, []) == :ok
    assert ExUnit.Filters.eval([os: :win], [], %{os: :unix}, []) == :ok
    assert ExUnit.Filters.eval([os: :win], [:os], %{}, []) == :ok
    assert ExUnit.Filters.eval([os: :win], [:os], %{os: :win}, []) == :ok

    assert ExUnit.Filters.eval([os: :win, os: :unix], [:os], %{os: :win}, []) == :ok
  end

  test "evaluating filters with skip" do
    assert ExUnit.Filters.eval([], [], %{}, []) == :ok
    assert ExUnit.Filters.eval([], [], %{skip: true}, []) == {:error, "due to skip tag"}
    assert ExUnit.Filters.eval([], [], %{skip: "skipped"}, []) == {:error, "skipped"}
    assert ExUnit.Filters.eval([], [:os], %{skip: "skipped"}, []) == {:error, "skipped"}
    assert ExUnit.Filters.eval([:skip], [], %{skip: true}, []) == :ok
    assert ExUnit.Filters.eval([:skip], [], %{skip: "skipped"}, []) == :ok
  end

  test "evaluating filters matches integers" do
    assert ExUnit.Filters.eval([int: "1"], [], %{int: 1}, []) == :ok
    assert ExUnit.Filters.eval([int: "1"], [int: 5], %{int: 1}, []) == :ok
    assert ExUnit.Filters.eval([int: "1"], [:int], %{int: 1}, []) == :ok
  end

  test "evaluating filter matches atoms" do
    assert ExUnit.Filters.eval([os: "win"], [], %{os: :win}, []) == :ok
    assert ExUnit.Filters.eval([os: "win"], [os: :unix], %{os: :win}, []) == :ok
    assert ExUnit.Filters.eval([os: "win"], [:os], %{os: :win}, []) == :ok
    assert ExUnit.Filters.eval([module: "Foo"], [:os], %{module: Foo}, []) == :ok
  end

  test "evaluating filter matches regexes" do
    assert ExUnit.Filters.eval([os: ~r"win"], [], %{os: :win}, []) == :ok

    assert ExUnit.Filters.eval([os: ~r"mac"], [os: :unix], %{os: :unix}, []) == {
             :error,
             "due to os filter"
           }
  end

  test "evaluating filter uses special rules for line" do
    tests = [
      %ExUnit.Test{tags: %{line: 3, describe_line: 2}},
      %ExUnit.Test{tags: %{line: 5, describe_line: nil}},
      %ExUnit.Test{tags: %{line: 8, describe_line: 7}},
      %ExUnit.Test{tags: %{line: 10, describe_line: 7}},
      %ExUnit.Test{tags: %{line: 13, describe_line: 12}}
    ]

    assert ExUnit.Filters.eval([line: "3"], [:line], %{line: 3, describe_line: 2}, tests) == :ok
    assert ExUnit.Filters.eval([line: "4"], [:line], %{line: 3, describe_line: 2}, tests) == :ok
    assert ExUnit.Filters.eval([line: "5"], [:line], %{line: 5, describe_line: nil}, tests) == :ok
    assert ExUnit.Filters.eval([line: "6"], [:line], %{line: 5, describe_line: nil}, tests) == :ok
    assert ExUnit.Filters.eval([line: "2"], [:line], %{line: 3, describe_line: 2}, tests) == :ok
    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 8, describe_line: 7}, tests) == :ok
    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 10, describe_line: 7}, tests) == :ok

    assert ExUnit.Filters.eval([line: "1"], [:line], %{line: 3, describe_line: 2}, tests) == {
             :error,
             "due to line filter"
           }

    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 3, describe_line: 2}, tests) == {
             :error,
             "due to line filter"
           }

    assert ExUnit.Filters.eval([line: "7"], [:line], %{line: 5, describe_line: nil}, tests) == {
             :error,
             "due to line filter"
           }
  end

  test "parsing filters" do
    assert ExUnit.Filters.parse(["run"]) == [:run]
    assert ExUnit.Filters.parse(["run:true"]) == [run: "true"]
    assert ExUnit.Filters.parse(["run:test"]) == [run: "test"]
    assert ExUnit.Filters.parse(["line:9"]) == [line: "9"]
  end

  test "file paths with line numbers" do
    assert ExUnit.Filters.parse_path("test/some/path.exs:123") == {
             "test/some/path.exs",
             [exclude: [:test], include: [line: "123"]]
           }

    assert ExUnit.Filters.parse_path("test/some/path.exs") == {"test/some/path.exs", []}

    assert ExUnit.Filters.parse_path("test/some/path.exs:123notreallyalinenumber123") == {
             "test/some/path.exs:123notreallyalinenumber123",
             []
           }

    assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123") == {
             "C:\\some\\path.exs",
             [exclude: [:test], include: [line: "123"]]
           }

    assert ExUnit.Filters.parse_path("C:\\some\\path.exs") == {"C:\\some\\path.exs", []}

    assert ExUnit.Filters.parse_path("C:\\some\\path.exs:123notreallyalinenumber123") == {
             "C:\\some\\path.exs:123notreallyalinenumber123",
             []
           }
  end
end


removes unnecessary testing fixtures

removes unnecessary testing fixtures

defmodule Geolix.TestHelpers.Fixture do
  @moduledoc false

  alias Geolix.TestData.MMDB2Fixture

  @path Path.join([__DIR__, "../fixtures"])
  @fixtures [
    {:fixture_anonymous, "GeoIP2-Anonymous-IP-Test.mmdb"},
    {:fixture_asn, "GeoLite2-ASN-Test.mmdb"},
    {:fixture_city, "GeoIP2-City-Test.mmdb"},
    {:fixture_connection, "GeoIP2-Connection-Type-Test.mmdb"},
    {:fixture_country, "GeoIP2-Country-Test.mmdb"},
    {:fixture_domain, "GeoIP2-Domain-Test.mmdb"},
    {:fixture_enterprise, "GeoIP2-Enterprise-Test.mmdb"},
    {:fixture_isp, "GeoIP2-ISP-Test.mmdb"},
    {:fixture_broken_pointers, "MaxMind-DB-test-broken-pointers-24.mmdb"},
    {:fixture_no_ipv4_search_tree, "MaxMind-DB-no-ipv4-search-tree.mmdb"}
  ]

  def download do
    Enum.each(@fixtures, fn {_name, file} ->
      MMDB2Fixture.download(file, @path)
    end)
  end

  def list, do: @fixtures
end

defmodule Geolix.TestHelpers.Fixture do
  @moduledoc false

  alias Geolix.TestData.MMDB2Fixture

  @path Path.join([__DIR__, "../fixtures"])
  @fixtures [
    {:fixture_anonymous, "GeoIP2-Anonymous-IP-Test.mmdb"},
    {:fixture_asn, "GeoLite2-ASN-Test.mmdb"},
    {:fixture_city, "GeoIP2-City-Test.mmdb"},
    {:fixture_connection, "GeoIP2-Connection-Type-Test.mmdb"},
    {:fixture_country, "GeoIP2-Country-Test.mmdb"},
    {:fixture_domain, "GeoIP2-Domain-Test.mmdb"},
    {:fixture_enterprise, "GeoIP2-Enterprise-Test.mmdb"},
    {:fixture_isp, "GeoIP2-ISP-Test.mmdb"},
    {:fixture_decoder, "MaxMind-DB-test-decoder.mmdb"},
    {:fixture_ipv4_24, "MaxMind-DB-test-ipv4-24.mmdb"},
    {:fixture_ipv4_28, "MaxMind-DB-test-ipv4-28.mmdb"},
    {:fixture_ipv4_32, "MaxMind-DB-test-ipv4-32.mmdb"},
    {:fixture_ipv6_24, "MaxMind-DB-test-ipv6-24.mmdb"},
    {:fixture_ipv6_28, "MaxMind-DB-test-ipv6-28.mmdb"},
    {:fixture_ipv6_32, "MaxMind-DB-test-ipv6-32.mmdb"},
    {:fixture_broken_pointers, "MaxMind-DB-test-broken-pointers-24.mmdb"},
    {:fixture_no_ipv4_search_tree, "MaxMind-DB-no-ipv4-search-tree.mmdb"}
  ]

  def download do
    Enum.each(@fixtures, fn {_name, file} ->
      MMDB2Fixture.download(file, @path)
    end)
  end

  def list, do: @fixtures
end


Enhance the tests to allow the bridge to work.

defmodule BaumeisterWeb.ProjectControllerTest do
  use BaumeisterWeb.ConnCase

  alias BaumeisterWeb.Project
  alias BaumeisterWeb.ProjectController

  @valid_attrs %{name: "some content", plugins: "some content", url: "some content",
    enabled: false, delay: 5}
  @invalid_attrs %{}

  @doc """
  Creates unique and valid attributes, i.e. the unique constraint on `name`
  is fullfilled by appending a unique integer.
  """
  def unique_attributes do
    count = "#{System.unique_integer([:positive])}"
    %{@valid_attrs | name: @valid_attrs.name <> count}
  end

  test "lists all entries on index", %{conn: conn} do
    conn = get conn, project_path(conn, :index)
    assert html_response(conn, 200) =~ "Listing projects"
  end

  test "renders form for new resources", %{conn: conn} do
    conn = get conn, project_path(conn, :new)
    assert html_response(conn, 200) =~ "New project"
  end

  test "creates resource and redirects when data is valid", %{conn: conn} do
    attributes = unique_attributes()
    conn = post conn, project_path(conn, :create), project: attributes
    assert redirected_to(conn) == project_path(conn, :index)
    assert Repo.get_by(Project, attributes)
  end

  test "does not create resource and renders errors when data is invalid", %{conn: conn} do
    conn = post conn, project_path(conn, :create), project: @invalid_attrs
    assert html_response(conn, 200) =~ "New project"
  end

  test "shows chosen resource", %{conn: conn} do
    project = Repo.insert! %Project{}
    conn = get conn, project_path(conn, :show, project)
    assert html_response(conn, 200) =~ "Show project"
  end

  test "renders page not found when id is nonexistent", %{conn: conn} do
    assert_error_sent 404, fn ->
      get conn, project_path(conn, :show, -1)
    end
  end

  test "renders form for editing chosen resource", %{conn: conn} do
    project = Repo.insert! %Project{}
    conn = get conn, project_path(conn, :edit, project)
    assert html_response(conn, 200) =~ "Edit project"
  end

  test "updates chosen resource and redirects when data is valid", %{conn: conn} do
    attributes = unique_attributes()
    {:ok, project} = ProjectController.insert(%Project{name: attributes[:name]})
    conn = put conn, project_path(conn, :update, project), project: attributes
    assert redirected_to(conn) == project_path(conn, :show, project)
    assert Repo.get_by(Project, attributes)
  end

  test "does not update chosen resource and renders errors when data is invalid", %{conn: conn} do
    project = Repo.insert! %Project{}
    conn = put conn, project_path(conn, :update, project), project: @invalid_attrs
    assert html_response(conn, 200) =~ "Edit project"
  end

  test "deletes chosen resource", %{conn: conn} do
    # project = Repo.insert! %Project{}
    attributes = unique_attributes()
    {:ok, project} = ProjectController.insert(%Project{name: attributes[:name]})
    conn = delete conn, project_path(conn, :delete, project)
    assert redirected_to(conn) == project_path(conn, :index)
    refute Repo.get(Project, project.id)
  end
end

defmodule BaumeisterWeb.ProjectControllerTest do
  use BaumeisterWeb.ConnCase

  alias BaumeisterWeb.Project
  @valid_attrs %{name: "some content", plugins: "some content", url: "some content",
    enabled: false, delay: 5}
  @invalid_attrs %{}

  @doc """
  Creates unique and valid attributes, i.e. the unique constraint on `name`
  is fullfilled by appending a unique integer.
  """
  def unique_attributes do
    count = "#{System.unique_integer([:positive])}"
    %{@valid_attrs | name: @valid_attrs.name <> count}
  end

  test "lists all entries on index", %{conn: conn} do
    conn = get conn, project_path(conn, :index)
    assert html_response(conn, 200) =~ "Listing projects"
  end

  test "renders form for new resources", %{conn: conn} do
    conn = get conn, project_path(conn, :new)
    assert html_response(conn, 200) =~ "New project"
  end

  test "creates resource and redirects when data is valid", %{conn: conn} do
    attributes = unique_attributes()
    conn = post conn, project_path(conn, :create), project: attributes
    assert redirected_to(conn) == project_path(conn, :index)
    assert Repo.get_by(Project, attributes)
  end

  test "does not create resource and renders errors when data is invalid", %{conn: conn} do
    conn = post conn, project_path(conn, :create), project: @invalid_attrs
    assert html_response(conn, 200) =~ "New project"
  end

  test "shows chosen resource", %{conn: conn} do
    project = Repo.insert! %Project{}
    conn = get conn, project_path(conn, :show, project)
    assert html_response(conn, 200) =~ "Show project"
  end

  test "renders page not found when id is nonexistent", %{conn: conn} do
    assert_error_sent 404, fn ->
      get conn, project_path(conn, :show, -1)
    end
  end

  test "renders form for editing chosen resource", %{conn: conn} do
    project = Repo.insert! %Project{}
    conn = get conn, project_path(conn, :edit, project)
    assert html_response(conn, 200) =~ "Edit project"
  end

  test "updates chosen resource and redirects when data is valid", %{conn: conn} do
    project = Repo.insert! %Project{}
    attributes = unique_attributes()
    conn = put conn, project_path(conn, :update, project), project: attributes
    assert redirected_to(conn) == project_path(conn, :show, project)
    assert Repo.get_by(Project, attributes)
  end

  test "does not update chosen resource and renders errors when data is invalid", %{conn: conn} do
    project = Repo.insert! %Project{}
    conn = put conn, project_path(conn, :update, project), project: @invalid_attrs
    assert html_response(conn, 200) =~ "Edit project"
  end

  test "deletes chosen resource", %{conn: conn} do
    project = Repo.insert! %Project{}
    conn = delete conn, project_path(conn, :delete, project)
    assert redirected_to(conn) == project_path(conn, :index)
    refute Repo.get(Project, project.id)
  end
end


Added test which proves input_for does not work with file_input (Bug #70)

defmodule Phoenix.HTML.InputsForTest do
  use ExUnit.Case, async: true

  import Phoenix.HTML
  import Phoenix.HTML.Form

  @doc """
  A function that executes `inputs_for/4` and
  extracts its inner contents for assertion.
  """
  def safe_inputs_for(field, opts \\ [multipart: false], fun) do
    mark = "--PLACEHOLDER--"
    {multipart, opts} = Keyword.pop(opts, :multipart, false)
    conn =
      Plug.Test.conn(:get, "/foo", %{"search" => %{
        "date" => %{"year" => "2020", "month" => "4", "day" => "17"},
        "dates" => %{"0" => %{"year" => "2010", "month" => "4", "day" => "17"},
                     "1" => %{"year" => "2020", "month" => "4", "day" => "17"}}
      }})

    contents =
      safe_to_string form_for(conn, "/", [as: :search, multipart: multipart], fn f ->
        html_escape [mark, inputs_for(f, field, opts, fun), mark]
      end)

    [_, inner, _] = String.split(contents, mark)
    inner
  end

  ## Cardinality one

  test "one: inputs_for/4 without default and field is not present" do
    contents =
      safe_inputs_for(:unknown, fn f ->
        refute f.index
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_year" name="search[unknown][year]" type="text">)
  end

  test "one: inputs_for/4 does not generate index" do
    safe_inputs_for(:unknown, fn f ->
      refute f.index
      "ok"
    end)
  end

  test "one: inputs_for/4 without default and field is present" do
    contents =
      safe_inputs_for(:date, fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_date_year" name="search[date][year]" type="text" value="2020">)
  end

  test "one: inputs_for/4 with default and field is not present" do
    contents =
      safe_inputs_for(:unknown, [default: %{year: 2015}], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_year" name="search[unknown][year]" type="text" value="2015">)
  end

  test "one: inputs_for/4 with default and field is present" do
    contents =
      safe_inputs_for(:date, [default: %{year: 2015}], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_date_year" name="search[date][year]" type="text" value="2020">)
  end

  test "one: inputs_for/4 with custom name and id" do
    contents =
      safe_inputs_for(:date, [as: :foo, id: :bar], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="bar_year" name="foo[year]" type="text" value="2020">)
  end

  ## Cardinality many

  test "many: inputs_for/4 with file field generates file input" do
    contents =
      safe_inputs_for(:unknown, [default: [%{}, %{}], multipart: true], fn f ->
        assert f.index in [0, 1]
        file_input f, :file
      end)

    assert contents ==
           ~s(<input id="search_unknown_0_file" name="search[unknown][0][file]" type="text">) <>
           ~s(<input id="search_unknown_1_file" name="search[unknown][1][file]" type="text">)
  end

  test "many: inputs_for/4 with default and field is not present" do
    contents =
      safe_inputs_for(:unknown, [default: [%{year: 2012}, %{year: 2018}]], fn f ->
        assert f.index in [0, 1]
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_0_year" name="search[unknown][0][year]" type="text" value="2012">) <>
           ~s(<input id="search_unknown_1_year" name="search[unknown][1][year]" type="text" value="2018">)
  end

  test "many: inputs_for/4 generates indexes" do
    safe_inputs_for(:unknown, [default: [%{year: 2012}]], fn f ->
      assert f.index == 0
      "ok"
    end)

    safe_inputs_for(:unknown, [default: [%{year: 2012}, %{year: 2018}]], fn f ->
      assert f.index in [0, 1]
      "ok"
    end)
  end

  test "many: inputs_for/4 with default and field is present" do
    contents =
      safe_inputs_for(:dates, [default: [%{year: 2012}, %{year: 2018}]], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_dates_0_year" name="search[dates][0][year]" type="text" value="2010">) <>
           ~s(<input id="search_dates_1_year" name="search[dates][1][year]" type="text" value="2020">)
  end

  test "many: inputs_for/4 with name and id" do
    contents =
      safe_inputs_for(:dates, [default: [%{year: 2012}, %{year: 2018}], as: :foo, id: :bar], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="bar_0_year" name="foo[0][year]" type="text" value="2010">) <>
           ~s(<input id="bar_1_year" name="foo[1][year]" type="text" value="2020">)
  end

  @prepend_append [prepend: [%{year: 2008}], append: [%{year: 2022}],
                   default: [%{year: 2012}, %{year: 2018}]]

  test "many: inputs_for/4 with prepend/append and field is not present" do
    contents =
      safe_inputs_for(:unknown, @prepend_append, fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_0_year" name="search[unknown][0][year]" type="text" value="2008">) <>
           ~s(<input id="search_unknown_1_year" name="search[unknown][1][year]" type="text" value="2012">) <>
           ~s(<input id="search_unknown_2_year" name="search[unknown][2][year]" type="text" value="2018">) <>
           ~s(<input id="search_unknown_3_year" name="search[unknown][3][year]" type="text" value="2022">)
  end

  test "many: inputs_for/4 with prepend/append and field is present" do
    contents =
      safe_inputs_for(:dates, @prepend_append, fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_dates_0_year" name="search[dates][0][year]" type="text" value="2010">) <>
           ~s(<input id="search_dates_1_year" name="search[dates][1][year]" type="text" value="2020">)
  end
end

defmodule Phoenix.HTML.InputsForTest do
  use ExUnit.Case, async: true

  import Phoenix.HTML
  import Phoenix.HTML.Form

  @doc """
  A function that executes `inputs_for/4` and
  extracts its inner contents for assertion.
  """
  def safe_inputs_for(field, opts \\ [], fun) do
    mark = "--PLACEHOLDER--"

    conn =
      Plug.Test.conn(:get, "/foo", %{"search" => %{
        "date" => %{"year" => "2020", "month" => "4", "day" => "17"},
        "dates" => %{"0" => %{"year" => "2010", "month" => "4", "day" => "17"},
                     "1" => %{"year" => "2020", "month" => "4", "day" => "17"}}
      }})

    contents =
      safe_to_string form_for(conn, "/", [as: :search], fn f ->
        html_escape [mark, inputs_for(f, field, opts, fun), mark]
      end)

    [_, inner, _] = String.split(contents, mark)
    inner
  end

  ## Cardinality one

  test "one: inputs_for/4 without default and field is not present" do
    contents =
      safe_inputs_for(:unknown, fn f ->
        refute f.index
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_year" name="search[unknown][year]" type="text">)
  end

  test "one: inputs_for/4 does not generate index" do
    safe_inputs_for(:unknown, fn f ->
      refute f.index
      "ok"
    end)
  end

  test "one: inputs_for/4 without default and field is present" do
    contents =
      safe_inputs_for(:date, fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_date_year" name="search[date][year]" type="text" value="2020">)
  end

  test "one: inputs_for/4 with default and field is not present" do
    contents =
      safe_inputs_for(:unknown, [default: %{year: 2015}], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_year" name="search[unknown][year]" type="text" value="2015">)
  end

  test "one: inputs_for/4 with default and field is present" do
    contents =
      safe_inputs_for(:date, [default: %{year: 2015}], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_date_year" name="search[date][year]" type="text" value="2020">)
  end

  test "one: inputs_for/4 with custom name and id" do
    contents =
      safe_inputs_for(:date, [as: :foo, id: :bar], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="bar_year" name="foo[year]" type="text" value="2020">)
  end

  ## Cardinality many

  test "many: inputs_for/4 with default and field is not present" do
    contents =
      safe_inputs_for(:unknown, [default: [%{year: 2012}, %{year: 2018}]], fn f ->
        assert f.index in [0, 1]
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_0_year" name="search[unknown][0][year]" type="text" value="2012">) <>
           ~s(<input id="search_unknown_1_year" name="search[unknown][1][year]" type="text" value="2018">)
  end

  test "many: inputs_for/4 generates indexes" do
    safe_inputs_for(:unknown, [default: [%{year: 2012}]], fn f ->
      assert f.index == 0
      "ok"
    end)

    safe_inputs_for(:unknown, [default: [%{year: 2012}, %{year: 2018}]], fn f ->
      assert f.index in [0, 1]
      "ok"
    end)
  end

  test "many: inputs_for/4 with default and field is present" do
    contents =
      safe_inputs_for(:dates, [default: [%{year: 2012}, %{year: 2018}]], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_dates_0_year" name="search[dates][0][year]" type="text" value="2010">) <>
           ~s(<input id="search_dates_1_year" name="search[dates][1][year]" type="text" value="2020">)
  end

  test "many: inputs_for/4 with name and id" do
    contents =
      safe_inputs_for(:dates, [default: [%{year: 2012}, %{year: 2018}], as: :foo, id: :bar], fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="bar_0_year" name="foo[0][year]" type="text" value="2010">) <>
           ~s(<input id="bar_1_year" name="foo[1][year]" type="text" value="2020">)
  end

  @prepend_append [prepend: [%{year: 2008}], append: [%{year: 2022}],
                   default: [%{year: 2012}, %{year: 2018}]]

  test "many: inputs_for/4 with prepend/append and field is not present" do
    contents =
      safe_inputs_for(:unknown, @prepend_append, fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_unknown_0_year" name="search[unknown][0][year]" type="text" value="2008">) <>
           ~s(<input id="search_unknown_1_year" name="search[unknown][1][year]" type="text" value="2012">) <>
           ~s(<input id="search_unknown_2_year" name="search[unknown][2][year]" type="text" value="2018">) <>
           ~s(<input id="search_unknown_3_year" name="search[unknown][3][year]" type="text" value="2022">)
  end

  test "many: inputs_for/4 with prepend/append and field is present" do
    contents =
      safe_inputs_for(:dates, @prepend_append, fn f ->
        text_input f, :year
      end)

    assert contents ==
           ~s(<input id="search_dates_0_year" name="search[dates][0][year]" type="text" value="2010">) <>
           ~s(<input id="search_dates_1_year" name="search[dates][1][year]" type="text" value="2020">)
  end
end


Fix bad pattern for first party farmware installer

defmodule Farmbot.CeleryScript.AST.Node.InstallFirstPartyFarmware do
  @moduledoc false
  use Farmbot.CeleryScript.AST.Node
  allow_args []

  @fpf_url "https://raw.githubusercontent.com/FarmBot-Labs/farmware_manifests/master/manifest.json"

  def execute(_, _, env) do
    env = mutate_env(env)
    case Farmbot.Farmware.Installer.add_repo(@fpf_url) do
      {:ok, _} -> do_sync_repo(env)
      {:error, :repo_already_exists} -> do_sync_repo(env)
      {:error, reason} -> {:error, reason, env}
    end
  end

  defp do_sync_repo(env) do
    case Farmbot.Farmware.Installer.sync_repo(@fpf_url) do
      {:ok, _} -> {:ok, env}
      {:error, reason} -> {:error, reason, env}
    end
  end
end

defmodule Farmbot.CeleryScript.AST.Node.InstallFirstPartyFarmware do
  @moduledoc false
  use Farmbot.CeleryScript.AST.Node
  allow_args []

  @fpf_url "https://raw.githubusercontent.com/FarmBot-Labs/farmware_manifests/master/manifest.json"

  def execute(_, _, env) do
    env = mutate_env(env)
    case Farmbot.Farmware.Installer.add_repo(@fpf_url) do
      {:ok, _} -> do_sync_repo(env)
      {:error, :repo_already_exists} -> do_sync_repo(env)
      {:error, reason} -> {:error, reason, env}
    end
  end

  defp do_sync_repo(env) do
    case Farmbot.Farmware.Installer.sync_repo(@fpf_url) do
      :ok -> {:ok, env}
      {:error, reason} -> {:error, reason, env}
    end
  end
end


Remove generated ExUnit test

Remove generated ExUnit test

defmodule ParticipateApi.AccountTest do
  use ParticipateApi.ModelCase

  alias ParticipateApi.Account

  @valid_attrs %{email: "some content", facebook_uid: "some content"}
  @invalid_attrs %{}

  test "changeset with valid attributes" do
    changeset = Account.changeset(%Account{}, @valid_attrs)
    assert changeset.valid?
  end

  test "changeset with invalid attributes" do
    changeset = Account.changeset(%Account{}, @invalid_attrs)
    refute changeset.valid?
  end
end


add a test for Ticket.to_map

add a test for Ticket.to_map

defmodule TicketPoker.TicketTest do
  use TicketPoker.ModelCase

  alias TicketPoker.Ticket

  @valid_attrs %{estimates: %{}, point_options: [], url: "some content"}
  @invalid_attrs %{}

  test "changeset with valid attributes" do
    changeset = Ticket.changeset(%Ticket{}, @valid_attrs)
    assert changeset.valid?
  end

  test "changeset with invalid attributes" do
    changeset = Ticket.changeset(%Ticket{}, @invalid_attrs)
    refute changeset.valid?
  end

  test "to_map" do
    ticket = %Ticket{
      id:             "foo",
      url:            "bar",
      point_options:  [1, 2, 3],
      estimates:      %{ "a" => 2, "b" => 3 }
    }

    assert Ticket.to_map(ticket) == %{
      estimates:      %{"a" => 2, "b" => 3},
      point_options:  [1, 2, 3]
    }
  end
end

defmodule TicketPoker.TicketTest do
  use TicketPoker.ModelCase

  alias TicketPoker.Ticket

  @valid_attrs %{estimates: %{}, point_options: [], url: "some content"}
  @invalid_attrs %{}

  test "changeset with valid attributes" do
    changeset = Ticket.changeset(%Ticket{}, @valid_attrs)
    assert changeset.valid?
  end

  test "changeset with invalid attributes" do
    changeset = Ticket.changeset(%Ticket{}, @invalid_attrs)
    refute changeset.valid?
  end
end


Add some changes to the previous code added in paper_trail_version_test file

defmodule PaperTrailTest.Version do
  use ExUnit.Case

  alias PaperTrail.Version
  alias PaperTrailTest.MultiTenantHelper, as: MultiTenant

  @valid_attrs %{
    event: "insert",
    item_type: "Person",
    item_id: 1,
    item_changes: %{first_name: "Izel", last_name: "Nakri"},
    origin: "test",
    inserted_at: DateTime.from_naive!(~N[1992-04-01 01:00:00.000], "Etc/UTC")
  }
  @invalid_attrs %{}

  @repo PaperTrail.RepoClient.repo

  setup_all do
    Application.put_env(:paper_trail, :strict_mode, false)
    Application.put_env(:paper_trail, :repo, PaperTrail.Repo)
    Code.eval_file("lib/paper_trail.ex")
    Code.eval_file("lib/version.ex")
    MultiTenant.setup_tenant(@repo)
    :ok
  end

  setup do
    @repo.delete_all(Version)
    Version
    |> MultiTenant.add_prefix_to_query()
    |> @repo.delete_all()
    on_exit fn ->
      @repo.delete_all(Version)
      Version
      |> MultiTenant.add_prefix_to_query()
      |> @repo.delete_all()
    end
    :ok
  end

  test "changeset with valid attributes" do
    changeset = Version.changeset(%Version{event: "insert"}, @valid_attrs)
    assert changeset.valid?
  end

  test "changeset without invalid attributes" do
    changeset = Version.changeset(%Version{event: "insert"}, @invalid_attrs)
    refute changeset.valid?
  end

  test "count works" do
    versions = add_three_versions()
    Version.count() == length(versions)
  end

  test "first works" do
    add_three_versions()
    Version.first() |> serialize == @valid_attrs
  end

  test "last works" do
    add_three_versions()
    Version.last() |> serialize != %{
      event: "insert",
      item_type: "Person",
      item_id: 3,
      item_changes: %{first_name: "Yukihiro", last_name: "Matsumoto"},
      origin: "test",
      inserted_at: DateTime.from_naive!(~N[1965-04-14 01:00:00.000], "Etc/UTC")
    }
  end

  # Multi tenant tests
  test "[multi tenant] count works" do
    versions = add_three_versions(MultiTenant.tenant())
    Version.count(prefix: MultiTenant.tenant()) == length(versions)
  end

  test "[multi tenant] first works" do
    add_three_versions(MultiTenant.tenant())
    Version.first(prefix: MultiTenant.tenant()) |> serialize == @valid_attrs
  end

  test "[multi tenant] last works" do
    add_three_versions(MultiTenant.tenant())
    Version.last(prefix: MultiTenant.tenant()) |> serialize != %{
      event: "insert",
      item_type: "Person",
      item_id: 3,
      item_changes: %{first_name: "Yukihiro", last_name: "Matsumoto"},
      origin: "test",
      inserted_at: DateTime.from_naive!(~N[1965-04-14 01:00:00.000], "Etc/UTC")
    }
  end

  def add_three_versions(prefix \\ nil) do
    @repo.insert_all(Version, [
      @valid_attrs,
      %{
        event: "insert",
        item_type: "Person",
        item_id: 2,
        item_changes: %{first_name: "Brendan", last_name: "Eich"},
        origin: "test",
        inserted_at: DateTime.from_naive!(~N[1961-07-04 01:00:00.000], "Etc/UTC")
      },
      %{
        event: "insert",
        item_type: "Person",
        item_id: 3,
        item_changes: %{first_name: "Yukihiro", last_name: "Matsumoto"},
        origin: "test",
        inserted_at: DateTime.from_naive!(~N[1965-04-14 01:00:00.000], "Etc/UTC")
      }
    ], returning: true, prefix: prefix) |> elem(1)
  end

  def serialize(nil), do: nil
  def serialize(resource) do
    relationships = resource.__struct__.__schema__(:associations)
    Map.drop(resource, [:__meta__, :__struct__] ++ relationships)
  end
end

defmodule PaperTrailTest.Version do
  use ExUnit.Case

  alias PaperTrail.Version
  alias PaperTrailTest.MultiTenantHelper, as: MultiTenant

  @valid_attrs %{
    event: "insert",
    item_type: "Person",
    item_id: 1,
    item_changes: %{first_name: "Izel", last_name: "Nakri"},
    origin: "test",
    inserted_at: DateTime.from_naive!(~N[1992-04-01 01:00:00.000], "Etc/UTC")
  }
  @invalid_attrs %{}

  @repo PaperTrail.RepoClient.repo

  setup_all do
    Application.put_env(:paper_trail, :strict_mode, false)
    Application.put_env(:paper_trail, :repo, PaperTrail.Repo)
    Code.eval_file("lib/paper_trail.ex")
    Code.eval_file("lib/version.ex")
    MultiTenant.setup_tenant(@repo)
    :ok
  end

  setup do
    @repo.delete_all(Version)
    Version
    |> MultiTenant.add_prefix_to_query()
    |> @repo.delete_all()
    on_exit fn ->
      @repo.delete_all(Version)
      Version
      |> MultiTenant.add_prefix_to_query()
      |> @repo.delete_all()
    end
    :ok
  end

  test "changeset with valid attributes" do
    changeset = Version.changeset(%Version{event: "insert"}, @valid_attrs)
    assert changeset.valid?
  end

  test "changeset without invalid attributes" do
    changeset = Version.changeset(%Version{event: "insert"}, @invalid_attrs)
    refute changeset.valid?
  end

  test "count works" do
    versions = add_three_versions()
    Version.count() == length(versions)
  end

  test "first works" do
    versions = add_three_versions()
    Version.first() |> serialize == @valid_attrs
  end

  test "last works" do
    versions = add_three_versions()
    Version.last() |> serialize != %{
      event: "insert",
      item_type: "Person",
      item_id: 3,
      item_changes: %{first_name: "Yukihiro", last_name: "Matsumoto"},
      origin: "test",
      inserted_at: DateTime.from_naive!(~N[1965-04-14 01:00:00.000], "Etc/UTC")
    }
  end

  # Multi tenant tests
  test "[multi tenant] count works" do
    versions = add_three_versions_multi()
    Version.count(prefix: MultiTenant.tenant()) == length(versions)
  end

  test "[multi tenant] first works" do
    versions = add_three_versions_multi()
    Version.first(prefix: MultiTenant.tenant()) |> serialize == @valid_attrs
  end

  test "[multi tenant] last works" do
    versions = add_three_versions_multi()
    Version.last(prefix: MultiTenant.tenant()) |> serialize != %{
      event: "insert",
      item_type: "Person",
      item_id: 3,
      item_changes: %{first_name: "Yukihiro", last_name: "Matsumoto"},
      origin: "test",
      inserted_at: DateTime.from_naive!(~N[1965-04-14 01:00:00.000], "Etc/UTC")
    }
  end

  def add_three_versions do
    @repo.insert_all(Version, [
      @valid_attrs,
      %{
        event: "insert",
        item_type: "Person",
        item_id: 2,
        item_changes: %{first_name: "Brendan", last_name: "Eich"},
        origin: "test",
        inserted_at: DateTime.from_naive!(~N[1961-07-04 01:00:00.000], "Etc/UTC")
      },
      %{
        event: "insert",
        item_type: "Person",
        item_id: 3,
        item_changes: %{first_name: "Yukihiro", last_name: "Matsumoto"},
        origin: "test",
        inserted_at: DateTime.from_naive!(~N[1965-04-14 01:00:00.000], "Etc/UTC")
      }
    ], returning: true) |> elem(1)
  end
  def add_three_versions_multi do
    @repo.insert_all(Version, [
      @valid_attrs,
      %{
        event: "insert",
        item_type: "Person",
        item_id: 2,
        item_changes: %{first_name: "Brendan", last_name: "Eich"},
        origin: "test",
        inserted_at: DateTime.from_naive!(~N[1961-07-04 01:00:00.000], "Etc/UTC")
      },
      %{
        event: "insert",
        item_type: "Person",
        item_id: 3,
        item_changes: %{first_name: "Yukihiro", last_name: "Matsumoto"},
        origin: "test",
        inserted_at: DateTime.from_naive!(~N[1965-04-14 01:00:00.000], "Etc/UTC")
      }
    ], returning: true, prefix: MultiTenant.tenant()) |> elem(1)
  end

  def serialize(nil), do: nil
  def serialize(resource) do
    relationships = resource.__struct__.__schema__(:associations)
    Map.drop(resource, [:__meta__, :__struct__] ++ relationships)
  end
end